From 83840957be81e8ce10e9555a42533c2ba5f76cd4 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 23 Jan 2011 11:52:28 +0000 Subject: [PATCH] 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' }