From 60b5d3853fc41cd17111287bfa2fff9b6bcab096 Mon Sep 17 00:00:00 2001 From: Hiroshi Miura Date: Fri, 14 Jan 2011 07:24:07 +0900 Subject: [PATCH 01/12] fix nikkei_sub economy --- resources/recipes/nikkei_sub_economy.recipe | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/recipes/nikkei_sub_economy.recipe b/resources/recipes/nikkei_sub_economy.recipe index 2dd8f1add8..8e7a68dfe7 100644 --- a/resources/recipes/nikkei_sub_economy.recipe +++ b/resources/recipes/nikkei_sub_economy.recipe @@ -27,6 +27,9 @@ class NikkeiNet_sub_economy(BasicNewsRecipe): {'class':"JSID_basePageMove JSID_baseAsyncSubmit cmn-form_area JSID_optForm_utoken"}, {'class':"cmn-article_keyword cmn-clearfix"}, {'class':"cmn-print_headline cmn-clearfix"}, + {'class':"cmn-article_list"}, + dict(id="ABOUT-NIKKEI"), + {'class':"cmn-sub_market"}, ] remove_tags_after = {'class':"cmn-pr_list"} From 4e5d5bbce0ed89e1ff33836ac2222243fbb0cb24 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 11:45:18 -0700 Subject: [PATCH 02/12] MOBI Input: SPecial case handling of emptu div tags with a defined height used as paragraph separators. Fixes #8391 (formatting issues when converting from .azw to .mobi. Not duplicating space between paragraphs.) --- src/calibre/ebooks/mobi/reader.py | 12 +++++++++++- src/calibre/manual/faq.rst | 5 +++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index e07418f41c..2f397006a1 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -542,7 +542,17 @@ class MobiReader(object): elif tag.tag == 'img': tag.set('height', height) else: - styles.append('margin-top: %s' % self.ensure_unit(height)) + if tag.tag == 'div' and not tag.text and \ + (not tag.tail or not tag.tail.strip()) and \ + not len(list(tag.iterdescendants())): + # Paragraph spacer + # Insert nbsp so that the element is never + # discarded by a renderer + tag.text = u'\u00a0' # nbsp + styles.append('height: %s' % + self.ensure_unit(height)) + else: + styles.append('margin-top: %s' % self.ensure_unit(height)) if attrib.has_key('width'): width = attrib.pop('width').strip() if width and re.search(r'\d+', width): diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 0e8c101620..ee72bf6fdb 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -450,6 +450,11 @@ How do I use purchased EPUB books with |app|? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Most purchased EPUB books have `DRM `_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your e-book reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|. +I am getting a "Permission Denied" error? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A permission denied error can occur because of many possible reasons, none of them having anything to do with |app|. You can get permission denied errors if you are using an SD card with write protect enabled. Or if you, or some program you used changed the file permissions of the files in question to read only. Or if there is a filesystem error on the device which caused your operating system to mount the filesystem in read only mode or mark a particular file as read only pending recovery. Or if the files have their owner set to a user other than you. You will need to fix the underlying cause of the permissions error before resuming to use |app|. Read the error message carefully, see what file it points to and fix the permissions on that file. + Can I have the comment metadata show up on my reader? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 91ccd9ad749a0ef88fff14971312c77d1f3838ae Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 12:31:19 -0700 Subject: [PATCH 03/12] Add AZW to the default list of internall viewed formats --- src/calibre/gui2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 6a9becee50..c94b99f141 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -85,7 +85,7 @@ def _config(): c.add_opt('LRF_ebook_viewer_options', default=None, help=_('Options for the LRF ebook viewer')) c.add_opt('internally_viewed_formats', default=['LRF', 'EPUB', 'LIT', - 'MOBI', 'PRC', 'HTML', 'FB2', 'PDB', 'RB', 'SNB'], + 'MOBI', 'PRC', 'AZW', 'HTML', 'FB2', 'PDB', 'RB', 'SNB'], help=_('Formats that are viewed using the internal viewer')) c.add_opt('column_map', default=ALL_COLUMNS, help=_('Columns to be displayed in the book list')) From 4d428dfa9a90023876413cab30f6d38fefdac620 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 12:34:49 -0700 Subject: [PATCH 04/12] Bulk metadata edit: Check apply date automatically whenever user changes the date in the pubdate field --- src/calibre/gui2/dialogs/metadata_bulk.py | 4 ++++ src/calibre/gui2/dialogs/metadata_bulk.ui | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index da6e92c26a..302766a92d 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -299,6 +299,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.pubdate.setDisplayFormat(pubdate_format) self.pubdate.setSpecialValueText(_('Undefined')) self.clear_pubdate_button.clicked.connect(self.clear_pubdate) + self.pubdate.dateChanged.connect(self.do_apply_pubdate) if len(self.db.custom_field_keys(include_composites=False)) == 0: self.central_widget.removeTab(1) @@ -315,6 +316,9 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.central_widget.setCurrentIndex(tab) self.exec_() + def do_apply_pubdate(self, *args): + self.apply_pubdate.setChecked(True) + def clear_pubdate(self, *args): self.pubdate.setDate(UNDEFINED_QDATE) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index b14c31c9d1..5690a8e555 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -367,6 +367,9 @@ from the value in the box MMM yyyy + + true + @@ -871,8 +874,8 @@ not multiple and the destination field is multiple 0 0 - 826 - 313 + 197 + 60 From 497b381bfa8ce1d5c8de65da03cc31b2b109ed10 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 12:45:11 -0700 Subject: [PATCH 05/12] The update found link now opens the update notification dialog instead of going straight to the download page --- src/calibre/gui2/init.py | 14 +++++++++++--- src/calibre/gui2/update.py | 9 ++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index fc70f0579d..95af265856 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -148,7 +148,6 @@ class StatusBar(QStatusBar): # {{{ self.get_version() + ' ' + _('created by Kovid Goyal') self.device_string = '' self.update_label = QLabel('') - self.update_label.setOpenExternalLinks(True) self.addPermanentWidget(self.update_label) self.update_label.setVisible(False) self._font = QFont() @@ -174,8 +173,9 @@ class StatusBar(QStatusBar): # {{{ self.clearMessage() def new_version_available(self, ver, url): - msg = (u'%s: %s') % ( - _('Update found'), url, ver) + msg = (u'%s: %s') % ( + _('Update found'), ver, ver) self.update_label.setText(msg) self.update_label.setCursor(Qt.PointingHandCursor) self.update_label.setVisible(True) @@ -240,6 +240,14 @@ class LayoutMixin(object): # {{{ self.status_bar.addPermanentWidget(button) self.status_bar.addPermanentWidget(self.jobs_button) self.setStatusBar(self.status_bar) + self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) + + def update_link_clicked(self, url): + print 11111111, url + url = unicode(url) + if url.startswith('update:'): + version = url.partition(':')[-1] + self.update_found(version, force=True) def finalize_layout(self): self.status_bar.initialize(self.system_tray_icon) diff --git a/src/calibre/gui2/update.py b/src/calibre/gui2/update.py index 30cfe8f5e4..9929d50a7e 100644 --- a/src/calibre/gui2/update.py +++ b/src/calibre/gui2/update.py @@ -52,8 +52,7 @@ class UpdateNotification(QDialog): self.label = QLabel('

'+ _('%s has been updated to version %s. ' 'See the new features. Visit the download pa' - 'ge?')%(__appname__, version)) + '">new features.')%(__appname__, version)) self.label.setOpenExternalLinks(True) self.label.setWordWrap(True) self.setWindowTitle(_('Update available!')) @@ -94,13 +93,13 @@ class UpdateMixin(object): type=Qt.QueuedConnection) self.update_checker.start() - def update_found(self, version): + def update_found(self, version, force=False): os = 'windows' if iswindows else 'osx' if isosx else 'linux' url = 'http://calibre-ebook.com/download_%s'%os self.status_bar.new_version_available(version, url) - if config.get('new_version_notification') and \ - dynamic.get('update to version %s'%version, True): + if force or (config.get('new_version_notification') and \ + dynamic.get('update to version %s'%version, True)): self._update_notification__ = UpdateNotification(version, parent=self) self._update_notification__.show() From 69c6ad02110d780f131aa41bf92317b8d82838fe Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 12:49:46 -0700 Subject: [PATCH 06/12] ... --- src/calibre/gui2/init.py | 1 - src/calibre/manual/conversion.rst | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 95af265856..ebd670c8fa 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -243,7 +243,6 @@ class LayoutMixin(object): # {{{ self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) def update_link_clicked(self, url): - print 11111111, url url = unicode(url) if url.startswith('update:'): version = url.partition(':')[-1] diff --git a/src/calibre/manual/conversion.rst b/src/calibre/manual/conversion.rst index 4b2b169d72..71639ca749 100644 --- a/src/calibre/manual/conversion.rst +++ b/src/calibre/manual/conversion.rst @@ -547,6 +547,7 @@ Some limitations of PDF input are: * Extraction of vector images and tables from within the document is also not supported. * Some PDFs use special glyphs to represent ll or ff or fi, etc. Conversion of these may or may not work depending on just how they are represented internally in the PDF. * Some PDFs store their images upside down with a rotation instruction, |app| currently doesn't support that instruction, so the images will be rotated in the output as well. + * Links and Tables of Contents are not supported To re-iterate **PDF is a really, really bad** format to use as input. If you absolutely must use PDF, then be prepared for an output ranging anywhere from decent to unusable, depending on the input PDF. From 188a96caeb39966d0cc5e9fa1bd91e0ef6a77ac4 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 16 Jan 2011 16:40:27 -0500 Subject: [PATCH 07/12] Make TagsLineEdit into a generic CompleteLineEdit class. Use CompleteLineEdit for Author completion in the main GUI window. --- src/calibre/gui2/convert/metadata.py | 2 +- src/calibre/gui2/convert/metadata.ui | 4 +- src/calibre/gui2/custom_column_widgets.py | 12 ++-- src/calibre/gui2/dialogs/metadata_bulk.py | 8 +-- src/calibre/gui2/dialogs/metadata_bulk.ui | 6 +- src/calibre/gui2/dialogs/metadata_single.py | 4 +- src/calibre/gui2/dialogs/metadata_single.ui | 4 +- src/calibre/gui2/dialogs/search.py | 2 +- src/calibre/gui2/dialogs/search.ui | 4 +- src/calibre/gui2/library/delegates.py | 54 +++++++++++++++++- src/calibre/gui2/library/views.py | 9 ++- src/calibre/gui2/widgets.py | 62 ++++++++++++--------- src/calibre/library/database.py | 3 + 13 files changed, 118 insertions(+), 56 deletions(-) diff --git a/src/calibre/gui2/convert/metadata.py b/src/calibre/gui2/convert/metadata.py index d3744bb614..de03033060 100644 --- a/src/calibre/gui2/convert/metadata.py +++ b/src/calibre/gui2/convert/metadata.py @@ -75,7 +75,7 @@ class MetadataWidget(Widget, Ui_Form): self.publisher.setCurrentIndex(self.publisher.findText(mi.publisher)) self.author_sort.setText(mi.author_sort if mi.author_sort else '') self.tags.setText(', '.join(mi.tags if mi.tags else [])) - self.tags.update_tags_cache(self.db.all_tags()) + self.tags.update_items_cache(self.db.all_tags()) self.comment.setPlainText(mi.comments if mi.comments else '') if mi.series: self.series.setCurrentIndex(self.series.findText(mi.series)) diff --git a/src/calibre/gui2/convert/metadata.ui b/src/calibre/gui2/convert/metadata.ui index a594f47b5d..5735193424 100644 --- a/src/calibre/gui2/convert/metadata.ui +++ b/src/calibre/gui2/convert/metadata.ui @@ -190,7 +190,7 @@ - + Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. @@ -310,7 +310,7 @@

widgets.h
- TagsLineEdit + CompleteLineEdit QLineEdit
widgets.h
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index ec18675359..d80909c4bb 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -14,7 +14,7 @@ from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \ QPushButton from calibre.utils.date import qt_to_dt, now -from calibre.gui2.widgets import TagsLineEdit, EnComboBox +from calibre.gui2.widgets import CompleteLineEdit, EnComboBox from calibre.gui2.comments_editor import Editor as CommentsEditor from calibre.gui2 import UNDEFINED_QDATE, error_dialog from calibre.utils.config import tweaks @@ -212,7 +212,7 @@ class Text(Base): values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) if self.col_metadata['is_multiple']: - w = TagsLineEdit(parent, values) + w = CompleteLineEdit(parent, values) w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) else: w = EnComboBox(parent) @@ -226,7 +226,7 @@ class Text(Base): val = self.normalize_db_val(val) if self.col_metadata['is_multiple']: self.setter(val) - self.widgets[1].update_tags_cache(self.all_values) + self.widgets[1].update_items_cache(self.all_values) else: idx = None for i, c in enumerate(self.all_values): @@ -656,7 +656,7 @@ class RemoveTags(QWidget): layout.setSpacing(5) layout.setContentsMargins(0, 0, 0, 0) - self.tags_box = TagsLineEdit(parent, values) + self.tags_box = CompleteLineEdit(parent, values) layout.addWidget(self.tags_box, stretch = 1) # self.tags_box.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) @@ -678,7 +678,7 @@ class BulkText(BulkBase): values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) if self.col_metadata['is_multiple']: - w = TagsLineEdit(parent, values) + w = CompleteLineEdit(parent, values) w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) self.widgets = [QLabel('&'+self.col_metadata['name']+': ' + _('tags to add'), parent), w] @@ -697,7 +697,7 @@ class BulkText(BulkBase): def initialize(self, book_ids): if self.col_metadata['is_multiple']: - self.widgets[1].update_tags_cache(self.all_values) + self.widgets[1].update_items_cache(self.all_values) else: val = self.get_initial_value(book_ids) self.initial_val = val = self.normalize_db_val(val) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 302766a92d..c7d5add912 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -279,8 +279,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.changed = False all_tags = self.db.all_tags() - self.tags.update_tags_cache(all_tags) - self.remove_tags.update_tags_cache(all_tags) + self.tags.update_items_cache(all_tags) + self.remove_tags.update_items_cache(all_tags) self.initialize_combos() @@ -751,8 +751,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): if d.result() == QDialog.Accepted: tag_string = ', '.join(d.tags) self.tags.setText(tag_string) - self.tags.update_tags_cache(self.db.all_tags()) - self.remove_tags.update_tags_cache(self.db.all_tags()) + self.tags.update_items_cache(self.db.all_tags()) + self.remove_tags.update_items_cache(self.db.all_tags()) def auto_number_changed(self, state): if state: diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 5690a8e555..b826f8d48d 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -195,7 +195,7 @@
- + Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. @@ -229,7 +229,7 @@ - + Comma separated list of tags to remove from the books. @@ -955,7 +955,7 @@ not multiple and the destination field is multiple
widgets.h
- TagsLineEdit + CompleteLineEdit QLineEdit
widgets.h
diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index e4efdf0470..139b8a2ebe 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -556,7 +556,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): tags = self.db.tags(row) self.original_tags = ', '.join(tags.split(',')) if tags else '' self.tags.setText(self.original_tags) - self.tags.update_tags_cache(self.db.all_tags()) + self.tags.update_items_cache(self.db.all_tags()) rating = self.db.rating(row) if rating > 0: self.rating.setValue(int(rating/2.)) @@ -776,7 +776,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): if d.result() == QDialog.Accepted: tag_string = ', '.join(d.tags) self.tags.setText(tag_string) - self.tags.update_tags_cache(self.db.all_tags()) + self.tags.update_items_cache(self.db.all_tags()) def fetch_metadata(self): diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 60c221be1a..b95267a618 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -335,7 +335,7 @@ If the box is colored green, then text matches the individual author's sort stri - + Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. @@ -842,7 +842,7 @@ If the box is colored green, then text matches the individual author's sort stri
widgets.h
- TagsLineEdit + CompleteLineEdit QLineEdit
widgets.h
diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py index 62a0f8a9f1..4f72fa915e 100644 --- a/src/calibre/gui2/dialogs/search.py +++ b/src/calibre/gui2/dialogs/search.py @@ -42,7 +42,7 @@ class SearchDialog(QDialog, Ui_Dialog): self.series_box.setAutoCompletionCaseSensitivity(Qt.CaseInsensitive) all_tags = db.all_tags() - self.tags_box.update_tags_cache(all_tags) + self.tags_box.update_items_cache(all_tags) self.box_last_values = copy.deepcopy(box_values) if self.box_last_values: diff --git a/src/calibre/gui2/dialogs/search.ui b/src/calibre/gui2/dialogs/search.ui index 6848a45506..519ae8c462 100644 --- a/src/calibre/gui2/dialogs/search.ui +++ b/src/calibre/gui2/dialogs/search.ui @@ -279,7 +279,7 @@
- + Enter tags separated by spaces @@ -360,7 +360,7 @@
widgets.h
- TagsLineEdit + CompleteLineEdit QLineEdit
widgets.h
diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index b41fd78dc3..af8f9c4d8a 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -16,7 +16,7 @@ from PyQt4.Qt import QColor, Qt, QModelIndex, QSize, \ QComboBox, QTextDocument from calibre.gui2 import UNDEFINED_QDATE, error_dialog -from calibre.gui2.widgets import EnLineEdit, TagsLineEdit +from calibre.gui2.widgets import EnLineEdit, CompleteLineEdit from calibre.utils.date import now, format_date from calibre.utils.config import tweaks from calibre.utils.formatter import validation_formatter @@ -173,9 +173,9 @@ class TagsDelegate(QStyledItemDelegate): # {{{ if self.db: col = index.model().column_map[index.column()] if not index.model().is_custom_column(col): - editor = TagsLineEdit(parent, self.db.all_tags()) + editor = CompleteLineEdit(parent, self.db.all_tags()) else: - editor = TagsLineEdit(parent, + editor = CompleteLineEdit(parent, sorted(list(self.db.all_custom(label=self.db.field_metadata.key_to_label(col))), key=sort_key)) return editor @@ -184,6 +184,54 @@ class TagsDelegate(QStyledItemDelegate): # {{{ return editor # }}} +class AuthorsDelegate(QStyledItemDelegate): # {{{ + def __init__(self, parent): + QStyledItemDelegate.__init__(self, parent) + self.db = None + + def set_database(self, db): + self.db = db + + def createEditor(self, parent, option, index): + if self.db: + col = index.model().column_map[index.column()] + if not index.model().is_custom_column(col): + editor = CompleteLineEdit(parent, self.db.all_author_names(), '&', True) + else: + editor = CompleteLineEdit(parent, + sorted(list(self.db.all_custom(label=self.db.field_metadata.key_to_label(col))), + key=sort_key), '&', True) + return editor + else: + editor = EnLineEdit(parent) + return editor +# }}} + +class CompleteDelegate(QStyledItemDelegate): # {{{ + def __init__(self, parent, sep, items_func_name, space_before_sep=False): + QStyledItemDelegate.__init__(self, parent) + self.sep = sep + self.items_func_name = items_func_name + self.space_before_sep = space_before_sep + + def set_database(self, db): + self.db = db + + def createEditor(self, parent, option, index): + if self.db and hasattr(self.db, self.items_func_name): + col = index.model().column_map[index.column()] + if not index.model().is_custom_column(col): + editor = CompleteLineEdit(parent, getattr(self.db, self.items_func_name)(), + self.sep, self.space_before_sep) + else: + editor = CompleteLineEdit(parent, + sorted(list(self.db.all_custom(label=self.db.field_metadata.key_to_label(col))), + key=sort_key), self.sep, self.space_before_sep) + else: + editor = EnLineEdit(parent) + return editor +# }}} + class CcDateDelegate(QStyledItemDelegate): # {{{ ''' Delegate for custom columns dates. Because this delegate stores the diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 3ff0fc3cd7..61161cd5e6 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -13,7 +13,7 @@ from PyQt4.Qt import QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, \ QPoint, QPixmap, QUrl, QImage, QPainter, QColor, QRect from calibre.gui2.library.delegates import RatingDelegate, PubDateDelegate, \ - TextDelegate, DateDelegate, TagsDelegate, CcTextDelegate, \ + TextDelegate, DateDelegate, CompleteDelegate, CcTextDelegate, \ CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcTemplateDelegate, \ CcEnumDelegate from calibre.gui2.library.models import BooksModel, DeviceBooksModel @@ -76,8 +76,8 @@ class BooksView(QTableView): # {{{ self.rating_delegate = RatingDelegate(self) self.timestamp_delegate = DateDelegate(self) self.pubdate_delegate = PubDateDelegate(self) - self.tags_delegate = TagsDelegate(self) - self.authors_delegate = TextDelegate(self) + self.tags_delegate = CompleteDelegate(self, ',', 'all_tags') + self.authors_delegate = CompleteDelegate(self, '&', 'all_author_names', True) self.series_delegate = TextDelegate(self) self.publisher_delegate = TextDelegate(self) self.text_delegate = TextDelegate(self) @@ -410,8 +410,7 @@ class BooksView(QTableView): # {{{ self.save_state() self._model.set_database(db) self.tags_delegate.set_database(db) - self.authors_delegate.set_auto_complete_function( - lambda: [(x, y.replace('|', ',')) for (x, y) in db.all_authors()]) + self.authors_delegate.set_database(db) self.series_delegate.set_auto_complete_function(db.all_series) self.publisher_delegate.set_auto_complete_function(db.all_publishers) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index f2ff783a76..6e2d52f835 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -426,46 +426,47 @@ class EnLineEdit(LineEditECM, QLineEdit): pass -class TagsCompleter(QCompleter): +class ItemsCompleter(QCompleter): ''' A completer object that completes a list of tags. It is used in conjunction with a CompleterLineEdit. ''' - def __init__(self, parent, all_tags): - QCompleter.__init__(self, all_tags, parent) - self.all_tags = set(all_tags) + def __init__(self, parent, all_items): + QCompleter.__init__(self, all_items, parent) + self.all_items = set(all_items) - def update(self, text_tags, completion_prefix): - tags = list(self.all_tags.difference(text_tags)) - model = QStringListModel(tags, self) + def update(self, text_items, completion_prefix): + items = list(self.all_items.difference(text_items)) + model = QStringListModel(items, self) self.setModel(model) self.setCompletionPrefix(completion_prefix) if completion_prefix.strip() != '': self.complete() - def update_tags_cache(self, tags): - self.all_tags = set(tags) - model = QStringListModel(tags, self) + def update_items_cache(self, items): + self.all_items = set(items) + model = QStringListModel(items, self) self.setModel(model) -class TagsLineEdit(EnLineEdit): +class CompleteLineEdit(EnLineEdit): ''' A QLineEdit that can complete parts of text separated by separator. ''' - def __init__(self, parent=0, tags=[]): + def __init__(self, parent=0, complete_items=[], sep=',', space_before_sep=False): EnLineEdit.__init__(self, parent) - self.separator = ',' + self.separator = sep + self.space_before_sep = space_before_sep self.connect(self, SIGNAL('textChanged(QString)'), self.text_changed) - self.completer = TagsCompleter(self, tags) + self.completer = ItemsCompleter(self, complete_items) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.connect(self, @@ -476,32 +477,43 @@ class TagsLineEdit(EnLineEdit): self.completer.setWidget(self) - def update_tags_cache(self, tags): - self.completer.update_tags_cache(tags) + def update_items_cache(self, complete_items): + self.completer.update_items_cache(complete_items) + + def set_separator(self, sep): + self.separator = sep + + def set_space_before_sep(self, space_before): + self.space_before_sep = space_before def text_changed(self, text): all_text = unicode(text) text = all_text[:self.cursorPosition()] - prefix = text.split(',')[-1].strip() + prefix = text.split(self.separator)[-1].strip() - text_tags = [] + text_items = [] for t in all_text.split(self.separator): t1 = unicode(t).strip() if t1 != '': - text_tags.append(t) - text_tags = list(set(text_tags)) + text_items.append(t) + text_items = list(set(text_items)) self.emit(SIGNAL('text_changed(PyQt_PyObject, PyQt_PyObject)'), - text_tags, prefix) + text_items, prefix) def complete_text(self, text): cursor_pos = self.cursorPosition() before_text = unicode(self.text())[:cursor_pos] after_text = unicode(self.text())[cursor_pos:] - prefix_len = len(before_text.split(',')[-1].strip()) - self.setText('%s%s%s %s' % (before_text[:cursor_pos - prefix_len], - text, self.separator, after_text)) - self.setCursorPosition(cursor_pos - prefix_len + len(text) + 2) + prefix_len = len(before_text.split(self.separator)[-1].strip()) + if self.space_before_sep: + complete_text_pat = '%s%s %s %s' + len_extra = 3 + else: + complete_text_pat = '%s%s%s %s' + len_extra = 2 + self.setText(complete_text_pat % (before_text[:cursor_pos - prefix_len], text, self.separator, after_text)) + self.setCursorPosition(cursor_pos - prefix_len + len(text) + len_extra) class EnComboBox(QComboBox): diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py index 6016dbd03e..e2ad8796a0 100644 --- a/src/calibre/library/database.py +++ b/src/calibre/library/database.py @@ -1059,6 +1059,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; def all_authors(self): return [ (i[0], i[1]) for i in \ self.conn.get('SELECT id, name FROM authors')] + + def all_author_names(self): + return [i[0].strip() for i in self.conn.get('SELECT name FROM authors') if i[0].strip()] def all_publishers(self): return [ (i[0], i[1]) for i in \ From cfa57f63df7e4fe8d683082e780b7df31c52fabb Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 16 Jan 2011 17:14:26 -0500 Subject: [PATCH 08/12] GUI: Have Authors combo boxes use completion using & just like tags use completion with , --- src/calibre/gui2/convert/metadata.py | 3 + src/calibre/gui2/convert/metadata.ui | 69 +++++++++------------ src/calibre/gui2/dialogs/metadata_bulk.py | 4 ++ src/calibre/gui2/dialogs/metadata_bulk.ui | 19 +++--- src/calibre/gui2/dialogs/metadata_single.py | 4 ++ src/calibre/gui2/dialogs/metadata_single.ui | 51 +++++++-------- src/calibre/gui2/dialogs/search.py | 3 + src/calibre/gui2/dialogs/search.ui | 13 ++-- src/calibre/gui2/widgets.py | 16 +++++ 9 files changed, 104 insertions(+), 78 deletions(-) diff --git a/src/calibre/gui2/convert/metadata.py b/src/calibre/gui2/convert/metadata.py index de03033060..5f39202e26 100644 --- a/src/calibre/gui2/convert/metadata.py +++ b/src/calibre/gui2/convert/metadata.py @@ -68,6 +68,9 @@ class MetadataWidget(Widget, Ui_Form): def initialize_metadata_options(self): self.initialize_combos() self.author.editTextChanged.connect(self.deduce_author_sort) + self.author.set_separator('&') + self.author.set_space_before_sep(True) + self.author.update_items_cache(self.db.all_author_names()) mi = self.db.get_metadata(self.book_id, index_is_id=True) self.title.setText(mi.title) diff --git a/src/calibre/gui2/convert/metadata.ui b/src/calibre/gui2/convert/metadata.ui index 5735193424..24b7c0904a 100644 --- a/src/calibre/gui2/convert/metadata.ui +++ b/src/calibre/gui2/convert/metadata.ui @@ -20,38 +20,8 @@ Book Cover - - - - - - - 0 - 0 - - - - - - - - - - Use cover from &source file - - - true - - - - - 6 - - - 0 - @@ -64,12 +34,6 @@ - - 6 - - - 0 - @@ -86,7 +50,7 @@ ... - + :/images/document_open.png:/images/document_open.png @@ -95,6 +59,30 @@ + + + + Use cover from &source file + + + true + + + + + + + + + + 0 + 0 + + + + + + opt_prefer_metadata_cover @@ -255,7 +243,7 @@
- + true @@ -320,6 +308,11 @@
calibre/gui2/widgets.h
1 + + CompleteComboBox + QComboBox +
widgets.h
+
title diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index c7d5add912..2b3a319663 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -726,6 +726,10 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): name = name.strip().replace('|', ',') self.authors.addItem(name) self.authors.setEditText('') + + self.authors.set_separator('&') + self.authors.set_space_before_sep(True) + self.authors.update_items_cache(self.db.all_author_names()) def initialize_series(self): all_series = self.db.all_series() diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index b826f8d48d..3950026325 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -14,7 +14,7 @@ Edit Meta information - + :/images/edit_input.png:/images/edit_input.png @@ -45,7 +45,7 @@ 0 0 842 - 589 + 553 @@ -76,7 +76,7 @@
- + true @@ -210,7 +210,7 @@ Open Tag Editor - + :/images/chapters.png:/images/chapters.png @@ -381,7 +381,7 @@ from the value in the box ... - + :/images/trash.png:/images/trash.png @@ -874,8 +874,8 @@ not multiple and the destination field is multiple 0 0 - 197 - 60 + 231 + 82 @@ -964,6 +964,11 @@ not multiple and the destination field is multiple QLineEdit
widgets.h
+ + CompleteComboBox + QComboBox +
widgets.h
+
authors diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 139b8a2ebe..4ca2072317 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -724,6 +724,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): au = _('Unknown') au = ' & '.join([a.strip().replace('|', ',') for a in au.split(',')]) self.authors.setEditText(au) + + self.authors.set_separator('&') + self.authors.set_space_before_sep(True) + self.authors.update_items_cache(self.db.all_author_names()) def initialize_series(self): self.series.setSizeAdjustPolicy(self.series.AdjustToContentsOnFirstShow) diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index b95267a618..b2f42937da 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -20,7 +20,7 @@ Edit Meta Information - + :/images/edit_input.png:/images/edit_input.png @@ -43,8 +43,8 @@ 0 0 - 986 - 677 + 955 + 665 @@ -125,7 +125,7 @@ Using this button to create title sort will change title sort from red to green. ... - + :/images/auto_author_sort.png:/images/auto_author_sort.png
@@ -152,7 +152,7 @@ Using this button to create title sort will change title sort from red to green. ... - + :/images/swap.png:/images/swap.png @@ -186,7 +186,7 @@ Using this button to create author sort will change author sort from red to gree ... - + :/images/auto_author_sort.png:/images/auto_author_sort.png
@@ -240,7 +240,7 @@ Using this button to create author sort will change author sort from red to gree
- + true @@ -352,7 +352,7 @@ If the box is colored green, then text matches the individual author's sort stri Open Tag Editor - + :/images/chapters.png:/images/chapters.png @@ -405,7 +405,7 @@ If the box is colored green, then text matches the individual author's sort stri ... - + :/images/trash.png:/images/trash.png @@ -491,7 +491,7 @@ If the box is colored green, then text matches the individual author's sort stri Clear published date - + :/images/trash.png:/images/trash.png @@ -550,15 +550,9 @@ If the box is colored green, then text matches the individual author's sort stri - - 6 - QLayout::SetMaximumSize - - 0 - @@ -571,19 +565,13 @@ If the box is colored green, then text matches the individual author's sort stri - - 6 - - - 0 - &Browse - + :/images/document_open.png:/images/document_open.png @@ -597,7 +585,7 @@ If the box is colored green, then text matches the individual author's sort stri T&rim - + :/images/trim.png:/images/trim.png @@ -611,7 +599,7 @@ If the box is colored green, then text matches the individual author's sort stri &Remove - + :/images/trash.png:/images/trash.png @@ -702,7 +690,7 @@ If the box is colored green, then text matches the individual author's sort stri ... - + :/images/add_book.png:/images/add_book.png @@ -722,7 +710,7 @@ If the box is colored green, then text matches the individual author's sort stri ... - + :/images/trash.png:/images/trash.png @@ -742,7 +730,7 @@ If the box is colored green, then text matches the individual author's sort stri ... - + :/images/book.png:/images/book.png @@ -762,7 +750,7 @@ If the box is colored green, then text matches the individual author's sort stri - + :/images/edit_input.png:/images/edit_input.png @@ -863,6 +851,11 @@ If the box is colored green, then text matches the individual author's sort stri
calibre/gui2/comments_editor.h
1 + + CompleteComboBox + QComboBox +
widgets.h
+
title diff --git a/src/calibre/gui2/dialogs/search.py b/src/calibre/gui2/dialogs/search.py index 4f72fa915e..95c7cf9225 100644 --- a/src/calibre/gui2/dialogs/search.py +++ b/src/calibre/gui2/dialogs/search.py @@ -31,6 +31,9 @@ class SearchDialog(QDialog, Ui_Dialog): self.authors_box.setEditText('') self.authors_box.completer().setCompletionMode(QCompleter.PopupCompletion) self.authors_box.setAutoCompletionCaseSensitivity(Qt.CaseInsensitive) + self.authors_box.set_separator('&') + self.authors_box.set_space_before_sep(True) + self.authors_box.update_items_cache(self.db.all_author_names()) all_series = db.all_series() all_series.sort(key=lambda x : sort_key(x[1])) diff --git a/src/calibre/gui2/dialogs/search.ui b/src/calibre/gui2/dialogs/search.ui index 519ae8c462..06ea9de379 100644 --- a/src/calibre/gui2/dialogs/search.ui +++ b/src/calibre/gui2/dialogs/search.ui @@ -6,15 +6,15 @@ 0 0 - 731 - 411 + 752 + 472
Advanced Search - + :/images/search.png:/images/search.png @@ -265,7 +265,7 @@
- + Enter an author's name. Only one author can be used. @@ -364,6 +364,11 @@ QLineEdit
widgets.h
+ + CompleteComboBox + QComboBox +
widgets.h
+
all diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 6e2d52f835..0bb5ee7634 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -540,6 +540,22 @@ class EnComboBox(QComboBox): idx = 0 self.setCurrentIndex(idx) +class CompleteComboBox(EnComboBox): + + def __init__(self, *args): + EnComboBox.__init__(self, *args) + self.setLineEdit(CompleteLineEdit(self)) + + def update_items_cache(self, complete_items): + self.lineEdit().update_items_cache(complete_items) + + def set_separator(self, sep): + self.lineEdit().set_separator(sep) + + def set_space_before_sep(self, space_before): + self.lineEdit().set_space_before_sep(space_before) + + class HistoryLineEdit(QComboBox): lost_focus = pyqtSignal() From ca0e545253e3d4b5a6bf641cac78e9b816f10d81 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 16:51:26 -0700 Subject: [PATCH 09/12] E-book viewer: Display cover when viewing FB2 files --- src/calibre/ebooks/fb2/input.py | 18 +++++++++++------- src/calibre/ebooks/oeb/iterator.py | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/calibre/ebooks/fb2/input.py b/src/calibre/ebooks/fb2/input.py index b019873d39..3d3ec69833 100644 --- a/src/calibre/ebooks/fb2/input.py +++ b/src/calibre/ebooks/fb2/input.py @@ -104,13 +104,17 @@ class FB2Input(InputFormatPlugin): entries = [(f, guess_type(f)[0]) for f in os.listdir('.')] opf.create_manifest(entries) opf.create_spine(['index.xhtml']) - - for img in doc.xpath('//f:coverpage/f:image', namespaces=NAMESPACES): - href = img.get('{%s}href'%XLINK_NS, img.get('href', None)) - if href is not None: - if href.startswith('#'): - href = href[1:] - opf.guide.set_cover(os.path.abspath(href)) + if mi.cover_data and mi.cover_data[1]: + with open('fb2_cover_calibre_mi.jpg', 'wb') as f: + f.write(mi.cover_data[1]) + opf.guide.set_cover(os.path.abspath('fb2_cover_calibre_mi.jpg')) + else: + for img in doc.xpath('//f:coverpage/f:image', namespaces=NAMESPACES): + href = img.get('{%s}href'%XLINK_NS, img.get('href', None)) + if href is not None: + if href.startswith('#'): + href = href[1:] + opf.guide.set_cover(os.path.abspath(href)) opf.render(open('metadata.opf', 'wb')) return os.path.join(os.getcwd(), 'metadata.opf') diff --git a/src/calibre/ebooks/oeb/iterator.py b/src/calibre/ebooks/oeb/iterator.py index 6820709b3e..08b4369078 100644 --- a/src/calibre/ebooks/oeb/iterator.py +++ b/src/calibre/ebooks/oeb/iterator.py @@ -227,7 +227,7 @@ class EbookIterator(object): self.log.warn('Missing spine item:', repr(spath)) cover = self.opf.cover - if self.ebook_ext in ('lit', 'mobi', 'prc', 'opf') and cover: + if self.ebook_ext in ('lit', 'mobi', 'prc', 'opf', 'fb2') and cover: cfile = os.path.join(self.base, 'calibre_iterator_cover.html') chtml = (TITLEPAGE%os.path.relpath(cover, self.base).replace(os.sep, '/')).encode('utf-8') From 7a79f8d98d2556803177769797e5b51e169e48a1 Mon Sep 17 00:00:00 2001 From: Shixin Zeng Date: Sun, 16 Jan 2011 19:43:13 -0600 Subject: [PATCH 10/12] make use_embedded_content settable per feed --- src/calibre/web/feeds/news.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index ee5b11c5f6..dd32d3749f 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -901,10 +901,7 @@ class BasicNewsRecipe(Recipe): if self.test: feeds = feeds[:2] self.has_single_feed = len(feeds) == 1 - - if self.use_embedded_content is None: - self.use_embedded_content = feeds[0].has_embedded_content() - + index = os.path.join(self.output_dir, 'index.html') html = self.feeds2index(feeds) @@ -939,7 +936,9 @@ class BasicNewsRecipe(Recipe): url = None if not url: continue - func, arg = (self.fetch_embedded_article, article) if self.use_embedded_content else \ + func, arg = (self.fetch_embedded_article, article) \ + if self.use_embedded_content or (self.use_embedded_content == None and feed.has_embedded_content()) \ + else \ ((self.fetch_obfuscated_article if self.articles_are_obfuscated \ else self.fetch_article), url) req = WorkRequest(func, (arg, art_dir, f, a, len(feed)), From 34da8b73ccf55b846c6e612f923682f1abe3f09c Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 16 Jan 2011 21:35:53 -0500 Subject: [PATCH 11/12] Fix bug #8381: reference \t and \T PML indents properly. --- src/calibre/ebooks/pml/pmlconverter.py | 35 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/calibre/ebooks/pml/pmlconverter.py b/src/calibre/ebooks/pml/pmlconverter.py index 10e5871d31..a0814ee0dd 100644 --- a/src/calibre/ebooks/pml/pmlconverter.py +++ b/src/calibre/ebooks/pml/pmlconverter.py @@ -34,18 +34,15 @@ class PML_HTMLizer(object): 'ra', 'c', 'r', - 't', 's', 'l', 'k', - 'T', 'FN', 'SB', ] STATES_VALUE_REQ = [ 'a', - 'T', 'FN', 'SB', ] @@ -96,8 +93,6 @@ class PML_HTMLizer(object): 'Sb': 'sb', 'c': 'c', 'r': 'r', - 't': 't', - 'T': 'T', 'i': 'i', 'I': 'i', 'u': 'u', @@ -133,8 +128,6 @@ class PML_HTMLizer(object): DIV_STATES = [ 'c', 'r', - 't', - 'T', 'FN', 'SB', ] @@ -255,8 +248,6 @@ class PML_HTMLizer(object): for key, val in self.state.items(): if val[0]: - if key == 'T': - self.state['T'][0] = False if key in self.DIV_STATES: div.append(key) elif key in self.SPAN_STATES: @@ -506,6 +497,9 @@ class PML_HTMLizer(object): self.toc = TOC() self.file_name = file_name + indent_state = {'t': False, 'T': False} + adv_indent_val = '' + for s in self.STATES: self.state[s] = [False, '']; @@ -515,6 +509,8 @@ class PML_HTMLizer(object): parsed = [] empty = True + basic_indent = indent_state['t'] + adv_indent = indent_state['T'] # Must use StringIO, cStringIO does not support unicode line = StringIO.StringIO(line) @@ -527,7 +523,7 @@ class PML_HTMLizer(object): if c == '\\': c = line.read(1) - if c in 'qcrtTiIuobBlk': + if c in 'qcriIuobBlk': text = self.process_code(c, line) elif c in 'FS': l = line.read(1) @@ -574,6 +570,15 @@ class PML_HTMLizer(object): elif c == 'w': empty = False text = '
' % self.code_value(line) + elif c == 't': + indent_state[c] = not indent_state[c] + if indent_state[c]: + basic_indent = True + elif c == 'T': + indent_state[c] = not indent_state[c] + if indent_state[c]: + adv_indent = True + adv_indent_val = self.code_value(line) elif c == '-': empty = False text = '­' @@ -590,6 +595,16 @@ class PML_HTMLizer(object): if not empty: text = self.end_line() parsed.append(text) + + if basic_indent: + parsed.insert(0, self.STATES_TAGS['t'][0]) + parsed.append(self.STATES_TAGS['t'][1]) + elif adv_indent: + parsed.insert(0, self.STATES_TAGS['T'][0] % adv_indent_val) + parsed.append(self.STATES_TAGS['T'][1]) + indent_state['T'] = False + adv_indent_val = '' + output.append(u''.join(parsed)) line.close() From 899263f3b34febbe3f7c1fc435017b14b502a802 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 20:37:04 -0700 Subject: [PATCH 12/12] Updated Nature News --- resources/recipes/freenature.recipe | 66 +++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/resources/recipes/freenature.recipe b/resources/recipes/freenature.recipe index cf06e7163d..0b287842ec 100644 --- a/resources/recipes/freenature.recipe +++ b/resources/recipes/freenature.recipe @@ -1,4 +1,5 @@ from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import Tag import re class NatureNews(BasicNewsRecipe): @@ -10,17 +11,76 @@ class NatureNews(BasicNewsRecipe): max_articles_per_feed = 50 no_stylesheets = True - remove_tags_before = dict(name='h1', attrs={'class':'heading entry-title'}) - remove_tags_after = dict(name='h2', attrs={'id':'comments'}) + keep_only_tags = [dict(name='div', attrs={'id':'content'})] +# remove_tags_before = dict(name='h1', attrs={'class':'heading entry-title'}) +# remove_tags_after = dict(name='h2', attrs={'id':'comments'}) remove_tags = [ dict(name='h2', attrs={'id':'comments'}), dict(attrs={'alt':'Advertisement'}), dict(name='div', attrs={'class':'ad'}), - ] + dict(attrs={'class':'Z3988'}), + dict(attrs={'class':['formatpublished','type-of-article','cleardiv','disclaimer','buttons','comments xoxo']}), + dict(name='a', attrs={'href':'#comments'}), + dict(name='h2',attrs={'class':'subheading plusicon icon-add-comment'}) + ] preprocess_regexps = [ (re.compile(r'

ADVERTISEMENT

', re.DOTALL|re.IGNORECASE), lambda match: ''), ] + extra_css = ''' + .author { text-align: right; font-size: small; line-height:1em; margin-top:0px; margin-left:0; margin-right:0; margin-bottom: 0; } + .imagedescription { font-size: small; font-style:italic; line-height:1em; margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; } + .imagecredit { font-size: x-small; font-style: normal; font-weight: bold} + ''' + feeds = [('Nature News', 'http://feeds.nature.com/news/rss/most_recent')] + def preprocess_html(self,soup): + # The author name is slightly buried - dig it up + author = soup.find('p', {'class':'byline'}) + if author: + # Find out the author's name + authornamediv = author.find('span',{'class':'author fn'}) + authornamelink = authornamediv.find('a') + if authornamelink: + authorname = authornamelink.contents[0] + else: + authorname = authornamediv.contents[0] + # Stick the author's name in the byline tag + tag = Tag(soup,'div') + tag['class'] = 'author' + tag.insert(0,authorname.strip()) + author.replaceWith(tag) + + # Change the intro from a p to a div + intro = soup.find('p',{'class':'intro'}) + if intro: + tag = Tag(soup,'div') + tag['class'] = 'intro' + tag.insert(0,intro.contents[0]) + intro.replaceWith(tag) + + # Change span class=imagedescription to div + descr = soup.find('span',{'class':'imagedescription'}) + if descr: + tag = Tag(soup,'div') + tag['class'] = 'imagedescription' + tag.insert(0,descr.renderContents()) + descr.replaceWith(tag) + + # The references are in a list, let's make them simpler + reflistcont = soup.find('ul',{'id':'article-refrences'}) + if reflistcont: + reflist = reflistcont.li.renderContents() + tag = Tag(soup,'div') + tag['class'] = 'article-references' + tag.insert(0,reflist) + reflistcont.replaceWith(tag) + + # Within the id=content div, we need to remove all the stuff after the end of the class=entry-content + entrycontent = soup.find('div',{'class':'entry-content'}) + for nextSibling in entrycontent.findNextSiblings(): + nextSibling.extract() + + return soup