From 83840957be81e8ce10e9555a42533c2ba5f76cd4 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 23 Jan 2011 11:52:28 +0000 Subject: [PATCH 1/9] tag browser changes: 1) make user categories a search target in the find box (move colon to front) 2) show empty categories 3) add an api to find the path to a category --- src/calibre/gui2/tag_view.py | 32 ++++++++++++++++++++------- src/calibre/library/database2.py | 31 +++++++++++++------------- src/calibre/library/field_metadata.py | 6 ++--- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index f6eac49426..36b5eea940 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -509,8 +509,8 @@ class TagsModel(QAbstractItemModel): # {{{ QAbstractItemModel.__init__(self, parent) # must do this here because 'QPixmap: Must construct a QApplication - # before a QPaintDevice'. The ':' in front avoids polluting either the - # user-defined categories (':' at end) or columns namespaces (no ':'). + # before a QPaintDevice'. The ':' at the end avoids polluting either the + # user-defined categories (':' at front) or columns namespaces (no ':'). iconmap = {} for key in category_icon_map: iconmap[key] = QIcon(I(category_icon_map[key])) @@ -681,7 +681,7 @@ class TagsModel(QAbstractItemModel): # {{{ tb_cats = self.db.field_metadata for user_cat in sorted(self.db.prefs.get('user_categories', {}).keys(), key=sort_key): - cat_name = user_cat+':' # add the ':' to avoid name collision + cat_name = ':' + user_cat # add the ':' to avoid name collision tb_cats.add_user_category(label=cat_name, name=user_cat) if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) @@ -988,7 +988,7 @@ class TagsModel(QAbstractItemModel): # {{{ if self.hidden_categories and self.categories[i] in self.hidden_categories: continue row_index += 1 - if key.endswith(':'): + if key.startswith(':'): # User category, so skip it. The tag will be marked in its real category continue category_item = self.root_item.children[row_index] @@ -1007,7 +1007,7 @@ class TagsModel(QAbstractItemModel): # {{{ ans.append('%s%s:"=%s"'%(prefix, category, tag.name)) return ans - def find_node(self, key, txt, start_path): + def find_item_node(self, key, txt, start_path): ''' Search for an item (a node) in the tags browser list that matches both the key (exact case-insensitive match) and txt (contains case- @@ -1061,6 +1061,22 @@ class TagsModel(QAbstractItemModel): # {{{ break return self.path_found + def find_category_node(self, key): + ''' + Search for an category node (a top-level node) in the tags browser list + that matches the key (exact case-insensitive match). Returns the path to + the node. Paths are as in find_item_node. + ''' + if not key: + return None + + for i in xrange(self.rowCount(QModelIndex())): + idx = self.index(i, 0, QModelIndex()) + ckey = idx.internalPointer().category_key + if strcmp(ckey, key) == 0: + return self.path_for_index(idx) + return None + def show_item_at_path(self, path, box=False): ''' Scroll the browser and open categories to show the item referenced by @@ -1347,15 +1363,15 @@ class TagBrowserWidget(QWidget): # {{{ self.search_button.setFocus(True) self.item_search.lineEdit().blockSignals(False) - colon = txt.find(':') key = None + colon = txt.rfind(':') if len(txt) > 2 else 0 if colon > 0: key = self.parent.library_view.model().db.\ field_metadata.search_term_to_field_key(txt[:colon]) txt = txt[colon+1:] - self.current_find_position = model.find_node(key, txt, - self.current_find_position) + self.current_find_position = \ + model.find_item_node(key, txt, self.current_find_position) if self.current_find_position: model.show_item_at_path(self.current_find_position, box=True) elif self.item_search.text(): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 3dc110c1c8..4b6ab247c5 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -319,7 +319,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.field_metadata.remove_dynamic_categories() tb_cats = self.field_metadata for user_cat in sorted(self.prefs.get('user_categories', {}).keys(), key=sort_key): - cat_name = user_cat+':' # add the ':' to avoid name collision + cat_name = ':' + user_cat # add the ':' to avoid name collision tb_cats.add_user_category(label=cat_name, name=user_cat) if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) @@ -1241,7 +1241,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if category in icon_map: icon = icon_map[label] else: - icon = icon_map[':custom'] + icon = icon_map['custom:'] icon_map[category] = icon datatype = cat['datatype'] @@ -1337,20 +1337,19 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if label in taglist and name in taglist[label]: items.append(taglist[label][name]) # else: do nothing, to not include nodes w zero counts - if len(items): - cat_name = user_cat+':' # add the ':' to avoid name collision - # Not a problem if we accumulate entries in the icon map - if icon_map is not None: - icon_map[cat_name] = icon_map[':user'] - if sort == 'popularity': - categories[cat_name] = \ - sorted(items, key=lambda x: x.count, reverse=True) - elif sort == 'name': - categories[cat_name] = \ - sorted(items, key=lambda x: sort_key(x.sort)) - else: - categories[cat_name] = \ - sorted(items, key=lambda x:x.avg_rating, reverse=True) + cat_name = ':' + user_cat # add the ':' to avoid name collision + # Not a problem if we accumulate entries in the icon map + if icon_map is not None: + icon_map[cat_name] = icon_map['user:'] + if sort == 'popularity': + categories[cat_name] = \ + sorted(items, key=lambda x: x.count, reverse=True) + elif sort == 'name': + categories[cat_name] = \ + sorted(items, key=lambda x: sort_key(x.sort)) + else: + categories[cat_name] = \ + sorted(items, key=lambda x:x.avg_rating, reverse=True) #### Finally, the saved searches category #### items = [] diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index 2a9b7e7003..e9402d1227 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -16,7 +16,7 @@ class TagsIcons(dict): ''' category_icons = ['authors', 'series', 'formats', 'publisher', 'rating', - 'news', 'tags', ':custom', ':user', 'search',] + 'news', 'tags', 'custom:', 'user:', 'search',] def __init__(self, icon_dict): for a in self.category_icons: if a not in icon_dict: @@ -31,8 +31,8 @@ category_icon_map = { 'rating' : 'rating.png', 'news' : 'news.png', 'tags' : 'tags.png', - ':custom' : 'column.png', - ':user' : 'drawer.png', + 'custom:' : 'column.png', + 'user:' : 'drawer.png', 'search' : 'search.png' } From 323ddfe8fd4542e4362097ae1778b7487861188e Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 23 Jan 2011 09:53:15 -0500 Subject: [PATCH 2/9] RgexBuilder: Addb back return when user declines to select a format. --- src/calibre/gui2/convert/regex_builder.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/gui2/convert/regex_builder.py b/src/calibre/gui2/convert/regex_builder.py index bdcbe3356d..bdd219d733 100644 --- a/src/calibre/gui2/convert/regex_builder.py +++ b/src/calibre/gui2/convert/regex_builder.py @@ -166,6 +166,8 @@ class RegexEdit(QWidget, Ui_Edit): def builder(self): bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self) + if bld.cancelled: + return if bld.exec_() == bld.Accepted: self.edit.setText(bld.regex.text()) From 6896e2530a8a6b5d864d29df5f82ef7002733d4d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Jan 2011 09:10:04 -0700 Subject: [PATCH 3/9] Everett Herald by 77jag5. Fixes #8534 (new newsfeed for Everett Herald) --- resources/recipes/everett_herald.recipe | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 resources/recipes/everett_herald.recipe diff --git a/resources/recipes/everett_herald.recipe b/resources/recipes/everett_herald.recipe new file mode 100644 index 0000000000..3d91836b48 --- /dev/null +++ b/resources/recipes/everett_herald.recipe @@ -0,0 +1,36 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1295088390(BasicNewsRecipe): + title = u'Everett Herald' + language = 'en' + __author__ = '77ja65' + oldest_article = 4 + max_articles_per_feed = 50 + no_stylesheets = True + masthead_url = 'http://heraldnet.com/images/hnet/jQueryComponents/jQueryNavigation/heraldnet_logo.png' + extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }' + + feeds = [(u'Local News', + u'http://heraldnet.com/section/RSS02&mime=xml'), + (u'Sports', u'http://heraldnet.com/section/RSS04&mime=xml'), + (u'Entertainment', + u'http://heraldnet.com/section/RSS07&mime=xml'), + (u'Life', u'http://heraldnet.com/section/RSS03&mime=xml'), + (u'Breaking News', + u'http://heraldnet.com/section/RSS34&mime=xml'), + (u'Seahawks', u'http://heraldnet.com/section/RSS22&mime=xml'), + (u'HeraldNet', u'http://heraldnet.com/section/RSS01&mime=xml'), + (u'Inside Everett', + u'http://heraldnet.com/section/RSS26&mime=xml') + ] + + def print_version(self, url): + return url + "&template=PrinterFriendly" + + extra_css = ''' + h1{font-family:Arial,Helvetica,sans-serif; font- + weight:bold;font-size:large;} + h2{font-family:Arial,Helvetica,sans-serif; font- + weight:normal;font-size:small;} + ''' + From 1c80e5e4d1618acbf92ec83b572ebcbf79b135fb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Jan 2011 10:01:08 -0700 Subject: [PATCH 4/9] Bulk metadata edit: Set initial date for the added date control to today --- src/calibre/gui2/dialogs/metadata_bulk.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 6e6b553dba..2c88556d7b 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -6,8 +6,7 @@ __copyright__ = '2008, Kovid Goyal ' import re, os from PyQt4.Qt import Qt, QDialog, QGridLayout, QVBoxLayout, QFont, QLabel, \ - pyqtSignal, QDialogButtonBox -from PyQt4 import QtGui + pyqtSignal, QDialogButtonBox, QDate, QLineEdit from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog from calibre.gui2.dialogs.tag_editor import TagEditor @@ -302,6 +301,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.pubdate.setSpecialValueText(_('Undefined')) self.clear_pubdate_button.clicked.connect(self.clear_pubdate) self.pubdate.dateChanged.connect(self.do_apply_pubdate) + self.adddate.setDate(QDate.currentDate()) self.adddate.setMinimumDate(UNDEFINED_QDATE) self.adddate.setSpecialValueText(_('Undefined')) self.clear_adddate_button.clicked.connect(self.clear_adddate) @@ -365,16 +365,16 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): offset = 10 self.s_r_number_of_books = min(10, len(self.ids)) for i in range(1,self.s_r_number_of_books+1): - w = QtGui.QLabel(self.tabWidgetPage3) + w = QLabel(self.tabWidgetPage3) w.setText(_('Book %d:')%i) self.testgrid.addWidget(w, i+offset, 0, 1, 1) - w = QtGui.QLineEdit(self.tabWidgetPage3) + w = QLineEdit(self.tabWidgetPage3) w.setReadOnly(True) name = 'book_%d_text'%i setattr(self, name, w) self.book_1_text.setObjectName(name) self.testgrid.addWidget(w, i+offset, 1, 1, 1) - w = QtGui.QLineEdit(self.tabWidgetPage3) + w = QLineEdit(self.tabWidgetPage3) w.setReadOnly(True) name = 'book_%d_result'%i setattr(self, name, w) From b5a4e263e86f13aa728816af1e772b3688c74f1d Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 23 Jan 2011 12:51:08 -0500 Subject: [PATCH 5/9] Implement ticket #7834: Enhance Send Specific Formats dialog to better display existing formats. --- src/calibre/gui2/device.py | 20 +++- .../gui2/dialogs/choose_format_device.py | 53 +++++++++ .../gui2/dialogs/choose_format_device.ui | 111 ++++++++++++++++++ 3 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 src/calibre/gui2/dialogs/choose_format_device.py create mode 100644 src/calibre/gui2/dialogs/choose_format_device.ui diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 28b5e178ac..8a0a368cd3 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -13,7 +13,7 @@ from calibre.customize.ui import available_input_formats, available_output_forma device_plugins from calibre.devices.interface import DevicePlugin from calibre.devices.errors import UserFeedback, OpenFeedback -from calibre.gui2.dialogs.choose_format import ChooseFormatDialog +from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog from calibre.utils.ipc.job import BaseJob from calibre.devices.scanner import DeviceScanner from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \ @@ -826,8 +826,22 @@ class DeviceMixin(object): # {{{ fmt = None if specific: - d = ChooseFormatDialog(self, _('Choose format to send to device'), - self.device_manager.device.settings().format_map) + formats = [] + aval_out_formats = available_output_formats() + format_count = {} + for row in rows: + for f in self.library_view.model().db.formats(row.row()).split(','): + f = f.lower() + if format_count.has_key(f): + format_count[f] += 1 + else: + format_count[f] = 1 + for f in self.device_manager.device.settings().format_map: + if f in format_count.keys(): + formats.append((f, _('%i of %i Books' % (format_count[f], len(rows))), True if f in aval_out_formats else False)) + elif f in aval_out_formats: + formats.append((f, _('0 of %i Books' % len(rows)), True)) + d = ChooseFormatDeviceDialog(self, _('Choose format to send to device'), formats) if d.exec_() != QDialog.Accepted: return if d.format(): diff --git a/src/calibre/gui2/dialogs/choose_format_device.py b/src/calibre/gui2/dialogs/choose_format_device.py new file mode 100644 index 0000000000..fde00bb70a --- /dev/null +++ b/src/calibre/gui2/dialogs/choose_format_device.py @@ -0,0 +1,53 @@ +__license__ = 'GPL v3' +__copyright__ = '2011, John Schember ' + +from PyQt4.Qt import QDialog, QTreeWidgetItem, QIcon, SIGNAL + +from calibre.gui2 import file_icon_provider +from calibre.gui2.dialogs.choose_format_device_ui import Ui_ChooseFormatDeviceDialog + +class ChooseFormatDeviceDialog(QDialog, Ui_ChooseFormatDeviceDialog): + + def __init__(self, window, msg, formats): + ''' + formats is a list of tuples: [(format, exists, convertable)]. + format: Lower case format identifier. E.G. mobi + exists: String representing the number of books that + exist in the format. + convertable: True if the format is a convertable format. + formats should be ordered in the device's preferred format ordering. + ''' + QDialog.__init__(self, window) + Ui_ChooseFormatDeviceDialog.__init__(self) + self.setupUi(self) + self.connect(self.formats, SIGNAL('activated(QModelIndex)'), + self.activated_slot) + + self.msg.setText(msg) + for i, (format, exists, convertable) in enumerate(formats): + t_item = QTreeWidgetItem() + t_item.setIcon(0, file_icon_provider().icon_from_ext(format.lower())) + t_item.setText(0, format.upper()) + t_item.setText(1, exists) + if convertable: + t_item.setIcon(2, QIcon(I('ok.png'))) + self.formats.addTopLevelItem(t_item) + if i == 0: + self.formats.setCurrentItem(t_item) + t_item.setSelected(True) + self.formats.resizeColumnToContents(2) + self.formats.resizeColumnToContents(1) + self.formats.resizeColumnToContents(0) + self.formats.header().resizeSection(0, self.formats.header().sectionSize(0) * 2) + self._format = None + + def activated_slot(self, *args): + self.accept() + + def format(self): + return self._format + + def accept(self): + self._format = unicode(self.formats.currentItem().text(0)) + return QDialog.accept(self) + diff --git a/src/calibre/gui2/dialogs/choose_format_device.ui b/src/calibre/gui2/dialogs/choose_format_device.ui new file mode 100644 index 0000000000..c52a728d16 --- /dev/null +++ b/src/calibre/gui2/dialogs/choose_format_device.ui @@ -0,0 +1,111 @@ + + + ChooseFormatDeviceDialog + + + + 0 + 0 + 507 + 377 + + + + Choose Format + + + + :/images/mimetypes/unknown.png:/images/mimetypes/unknown.png + + + + + + TextLabel + + + + + + + true + + + + 64 + 64 + + + + true + + + + Format + + + + + Existing + + + AlignLeft|AlignVCenter + + + + + Convertable + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + ChooseFormatDeviceDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ChooseFormatDeviceDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + From 4cefbf0aa80280e323bad0b56a07e9300d5086af Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 23 Jan 2011 12:53:41 -0500 Subject: [PATCH 6/9] Fix spelling error. --- src/calibre/gui2/dialogs/choose_format_device.py | 6 +++--- src/calibre/gui2/dialogs/choose_format_device.ui | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/dialogs/choose_format_device.py b/src/calibre/gui2/dialogs/choose_format_device.py index fde00bb70a..d51040ef44 100644 --- a/src/calibre/gui2/dialogs/choose_format_device.py +++ b/src/calibre/gui2/dialogs/choose_format_device.py @@ -10,11 +10,11 @@ class ChooseFormatDeviceDialog(QDialog, Ui_ChooseFormatDeviceDialog): def __init__(self, window, msg, formats): ''' - formats is a list of tuples: [(format, exists, convertable)]. + formats is a list of tuples: [(format, exists, convertible)]. format: Lower case format identifier. E.G. mobi exists: String representing the number of books that exist in the format. - convertable: True if the format is a convertable format. + convertible: True if the format is a convertible format. formats should be ordered in the device's preferred format ordering. ''' QDialog.__init__(self, window) @@ -29,7 +29,7 @@ class ChooseFormatDeviceDialog(QDialog, Ui_ChooseFormatDeviceDialog): t_item.setIcon(0, file_icon_provider().icon_from_ext(format.lower())) t_item.setText(0, format.upper()) t_item.setText(1, exists) - if convertable: + if convertible: t_item.setIcon(2, QIcon(I('ok.png'))) self.formats.addTopLevelItem(t_item) if i == 0: diff --git a/src/calibre/gui2/dialogs/choose_format_device.ui b/src/calibre/gui2/dialogs/choose_format_device.ui index c52a728d16..d527296144 100644 --- a/src/calibre/gui2/dialogs/choose_format_device.ui +++ b/src/calibre/gui2/dialogs/choose_format_device.ui @@ -54,7 +54,7 @@ - Convertable + Convertible From 199895ac7570b12bb71be56026cfc88e04d7c20e Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 23 Jan 2011 12:54:08 -0500 Subject: [PATCH 7/9] ... --- src/calibre/gui2/dialogs/choose_format_device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/choose_format_device.py b/src/calibre/gui2/dialogs/choose_format_device.py index d51040ef44..f5e0761e4f 100644 --- a/src/calibre/gui2/dialogs/choose_format_device.py +++ b/src/calibre/gui2/dialogs/choose_format_device.py @@ -24,7 +24,7 @@ class ChooseFormatDeviceDialog(QDialog, Ui_ChooseFormatDeviceDialog): self.activated_slot) self.msg.setText(msg) - for i, (format, exists, convertable) in enumerate(formats): + for i, (format, exists, convertible) in enumerate(formats): t_item = QTreeWidgetItem() t_item.setIcon(0, file_icon_provider().icon_from_ext(format.lower())) t_item.setText(0, format.upper()) From 26cd80fe1adea36d9b11a8b6500e99a1bae4eb67 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 23 Jan 2011 18:14:15 +0000 Subject: [PATCH 8/9] Change user category indicator from ':' to '@' --- src/calibre/gui2/tag_view.py | 8 ++++---- src/calibre/library/database2.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 00f34aa171..d68be3b7d6 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -515,8 +515,8 @@ class TagsModel(QAbstractItemModel): # {{{ QAbstractItemModel.__init__(self, parent) # must do this here because 'QPixmap: Must construct a QApplication - # before a QPaintDevice'. The ':' at the end avoids polluting either the - # user-defined categories (':' at front) or columns namespaces (no ':'). + # before a QPaintDevice'. The ':' at the end avoids polluting either of + # the other namespaces (alpha, '#', or '@') iconmap = {} for key in category_icon_map: iconmap[key] = QIcon(I(category_icon_map[key])) @@ -690,7 +690,7 @@ class TagsModel(QAbstractItemModel): # {{{ tb_cats = self.db.field_metadata for user_cat in sorted(self.db.prefs.get('user_categories', {}).keys(), key=sort_key): - cat_name = ':' + user_cat # add the ':' to avoid name collision + cat_name = '@' + user_cat # add the '@' to avoid name collision tb_cats.add_user_category(label=cat_name, name=user_cat) if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) @@ -997,7 +997,7 @@ class TagsModel(QAbstractItemModel): # {{{ if self.hidden_categories and self.categories[i] in self.hidden_categories: continue row_index += 1 - if key.startswith(':'): + if key.startswith('@'): # User category, so skip it. The tag will be marked in its real category continue category_item = self.root_item.children[row_index] diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index e0ddcfd7c8..5638bad1ee 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -319,7 +319,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.field_metadata.remove_dynamic_categories() tb_cats = self.field_metadata for user_cat in sorted(self.prefs.get('user_categories', {}).keys(), key=sort_key): - cat_name = ':' + user_cat # add the ':' to avoid name collision + cat_name = '@' + user_cat # add the '@' to avoid name collision tb_cats.add_user_category(label=cat_name, name=user_cat) if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) @@ -1339,7 +1339,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if label in taglist and name in taglist[label]: items.append(taglist[label][name]) # else: do nothing, to not include nodes w zero counts - cat_name = ':' + user_cat # add the ':' to avoid name collision + cat_name = '@' + user_cat # add the '@' to avoid name collision # Not a problem if we accumulate entries in the icon map if icon_map is not None: icon_map[cat_name] = icon_map['user:'] From f95cc7eddc02153cc280e7f93dab695b388e7a86 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 23 Jan 2011 16:58:57 -0700 Subject: [PATCH 9/9] Heuristics, italicize common cases: Enhance pattern matching to match punctuation after pattern. --- src/calibre/ebooks/conversion/utils.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py index aabb1b8bc4..ad7f5f117d 100644 --- a/src/calibre/ebooks/conversion/utils.py +++ b/src/calibre/ebooks/conversion/utils.py @@ -137,17 +137,17 @@ class HeuristicProcessor(object): ] ITALICIZE_STYLE_PATS = [ - r'(?msu)(?<=\s)_(?P\S[^_]{0,40}?\S)?_(?=\s)', - r'(?msu)(?<=\s)/(?P\S[^/]{0,40}?\S)?/(?=\s)', - r'(?msu)(?<=\s)~~(?P\S[^~]{0,40}?\S)?~~(?=\s)', - r'(?msu)(?<=\s)\*(?P\S[^\*]{0,40}?\S)?\*(?=\s)', - r'(?msu)(?<=\s)~(?P\S[^~]{0,40}?\S)?~(?=\s)', - r'(?msu)(?<=\s)_/(?P\S[^/_]{0,40}?\S)?/_(?=\s)', - r'(?msu)(?<=\s)_\*(?P\S[^\*_]{0,40}?\S)?\*_(?=\s)', - r'(?msu)(?<=\s)\*/(?P\S[^/\*]{0,40}?\S)?/\*(?=\s)', - r'(?msu)(?<=\s)_\*/(?P\S[^\*_]{0,40}?\S)?/\*_(?=\s)', - r'(?msu)(?<=\s)/:(?P\S[^:/]{0,40}?\S)?:/(?=\s)', - r'(?msu)(?<=\s)\|:(?P\S[^:\|]{0,40}?\S)?:\|(?=\s)', + r'(?msu)(?<=\s)_(?P\S[^_]{0,40}?\S)?_(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)/(?P\S[^/]{0,40}?\S)?/(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)~~(?P\S[^~]{0,40}?\S)?~~(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)\*(?P\S[^\*]{0,40}?\S)?\*(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)~(?P\S[^~]{0,40}?\S)?~(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)_/(?P\S[^/_]{0,40}?\S)?/_(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)_\*(?P\S[^\*_]{0,40}?\S)?\*_(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)\*/(?P\S[^/\*]{0,40}?\S)?/\*(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)_\*/(?P\S[^\*_]{0,40}?\S)?/\*_(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)/:(?P\S[^:/]{0,40}?\S)?:/(?=[\s\.,\!\?])', + r'(?msu)(?<=\s)\|:(?P\S[^:\|]{0,40}?\S)?:\|(?=[\s\.,\!\?])', ] for word in ITALICIZE_WORDS: