From 65e6102e3317d3e20d118837eff41efbd3e28b2e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 24 Jan 2011 09:15:05 +0000 Subject: [PATCH] Add right-click category search support to the tags browser --- src/calibre/gui2/tag_view.py | 14 +++++++++++++- src/calibre/library/caches.py | 25 ++++++++++++++++++++++++- src/calibre/library/database2.py | 2 +- src/calibre/library/field_metadata.py | 11 +++++++---- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index d68be3b7d6..8b574948ff 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -186,7 +186,7 @@ class TagsView(QTreeView): # {{{ self.clear() def context_menu_handler(self, action=None, category=None, - key=None, index=None): + key=None, index=None, negate=None): if not action: return try: @@ -199,6 +199,9 @@ class TagsView(QTreeView): # {{{ if action == 'manage_categories': self.user_category_edit.emit(category) return + if action == 'search_category': + self.tags_marked.emit(category + ':' + str(not negate)) + return if action == 'manage_searches': self.saved_search_edit.emit(category) return @@ -268,6 +271,15 @@ class TagsView(QTreeView): # {{{ m.addAction(col, partial(self.context_menu_handler, action='show', category=col)) + # search by category + self.context_menu.addAction( + _('Search for books in category %s')%category, + partial(self.context_menu_handler, action='search_category', + category=key, negate=False)) + self.context_menu.addAction( + _('Search for books not in category %s')%category, + partial(self.context_menu_handler, action='search_category', + category=key, negate=True)) # Offer specific editors for tags/series/publishers/saved searches self.context_menu.addSeparator() if key in ['tags', 'publisher', 'series'] or \ diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 291d71f572..9f10d9f890 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -172,8 +172,9 @@ class ResultCache(SearchQueryParser): # {{{ ''' Stores sorted and filtered metadata in memory. ''' - def __init__(self, FIELD_MAP, field_metadata): + def __init__(self, FIELD_MAP, field_metadata, db_prefs = None): self.FIELD_MAP = FIELD_MAP + self.db_prefs = db_prefs self.composites = {} for key in field_metadata: if field_metadata[key]['datatype'] == 'composite': @@ -405,6 +406,22 @@ class ResultCache(SearchQueryParser): # {{{ matches.add(item[0]) return matches + def get_user_category_matches(self, location, query, candidates): + res = set([]) + if self.db_prefs is None: + return res + user_cats = self.db_prefs['user_categories'] + if location not in user_cats: + return res + c = set(candidates) + for (item, category, ign) in user_cats[location]: + s = self.get_matches(category, '=' + item, candidates=c) + c -= s + res |= s + if query == 'false': + return candidates - res + return res + def get_matches(self, location, query, allow_recursion=True, candidates=None): matches = set([]) if candidates is None: @@ -443,6 +460,10 @@ class ResultCache(SearchQueryParser): # {{{ return self.get_numeric_matches(location, query[1:], candidates, val_func=vf) + # check for user categories + if len(location) >= 2 and location.startswith('@'): + return self.get_user_category_matches(location[1:], query.lower(), + candidates) # everything else, or 'all' matches matchkind = CONTAINS_MATCH if (len(query) > 1): @@ -468,6 +489,8 @@ class ResultCache(SearchQueryParser): # {{{ for x in range(len(self.FIELD_MAP)): col_datatype.append('') for x in self.field_metadata: + if x.startswith('@'): + continue if len(self.field_metadata[x]['search_terms']): db_col[x] = self.field_metadata[x]['rec_index'] if self.field_metadata[x]['datatype'] not in \ diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 5638bad1ee..f2b2c94e31 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -332,7 +332,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): traceback.print_exc() self.book_on_device_func = None - self.data = ResultCache(self.FIELD_MAP, self.field_metadata) + self.data = ResultCache(self.FIELD_MAP, self.field_metadata, db_prefs=self.prefs) self.search = self.data.search self.search_getting_ids = self.data.search_getting_ids self.refresh = functools.partial(self.data.refresh, self) diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index e9402d1227..78fe899fa8 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -475,6 +475,8 @@ class FieldMetadata(dict): val = self._tb_cats[key] if val['is_category'] and val['kind'] in ('user', 'search'): del self._tb_cats[key] + if key in self._search_term_map: + del self._search_term_map[key] def cc_series_index_column_for(self, key): return self._tb_cats[key]['rec_index'] + 1 @@ -482,11 +484,12 @@ class FieldMetadata(dict): def add_user_category(self, label, name): if label in self._tb_cats: raise ValueError('Duplicate user field [%s]'%(label)) - self._tb_cats[label] = {'table':None, 'column':None, - 'datatype':None, 'is_multiple':None, - 'kind':'user', 'name':name, - 'search_terms':[], 'is_custom':False, + self._tb_cats[label] = {'table':None, 'column':None, + 'datatype':None, 'is_multiple':None, + 'kind':'user', 'name':name, + 'search_terms':[label],'is_custom':False, 'is_category':True} + self._add_search_terms_to_map(label, [label]) def add_search_category(self, label, name): if label in self._tb_cats: