diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index 43edd1c690..fb4fff9dc7 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -96,3 +96,17 @@ auto_connect_to_folder = '' # Specify a tag to be automatically applied when a book is added to the library. # Example: add_tag_to_new_books='ToDo' add_tag_to_new_books = '' + + +# Create search terms to apply a query across several built-in search terms. +# Syntax: {'new term':['existing term 1', 'term 2', ...], 'new':['old'...] ...} +# Example: create the term 'myseries' that when used as myseries:foo would +# search all of the search categories 'series', '#myseries', and '#myseries2': +# grouped_search_terms={'myseries':['series','#myseries', '#myseries2']} +# Example: two search terms 'a' and 'b' both that search 'tags' and '#mytags': +# grouped_search_terms={'a':['tags','#mytags'], 'b':['tags','#mytags']} +# Note: You cannot create a search term that is a duplicate of an existing term. +# Such duplicates will be silently ignored. Also note that search terms ignore +# case. 'MySearch' and 'mysearch' are the same term. +grouped_search_terms = {} + diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 89008735fe..e442007c9a 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -236,8 +236,8 @@ class BooksModel(QAbstractTableModel): # {{{ def search(self, text, reset=True): try: self.db.search(text) - except ParseException: - self.searched.emit(False) + except ParseException as e: + self.searched.emit(e.msg) return self.last_search = text if reset: diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 8986346dbc..d8f59b8db7 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -90,6 +90,7 @@ class SearchBox2(QComboBox): self.setSizeAdjustPolicy(self.AdjustToMinimumContentsLengthWithIcon) self.setMinimumContentsLength(25) self._in_a_search = False + self.tool_tip_text = self.toolTip() def initialize(self, opt_name, colorize=False, help_text=_('Search')): self.as_you_type = config['search_as_you_type'] @@ -100,6 +101,7 @@ class SearchBox2(QComboBox): self.clear_to_help() def normalize_state(self): + self.setToolTip(self.tool_tip_text) if self.help_state: self.setEditText('') self.line_edit.setStyleSheet( @@ -112,6 +114,7 @@ class SearchBox2(QComboBox): self.normal_background) def clear_to_help(self): + self.setToolTip(self.tool_tip_text) if self.help_state: return self.help_state = True @@ -131,6 +134,9 @@ class SearchBox2(QComboBox): self.clear_to_help() def search_done(self, ok): + if isinstance(ok, basestring): + self.setToolTip(ok) + ok = False if not unicode(self.currentText()).strip(): return self.clear_to_help() self._in_a_search = ok diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index ca66d28ddb..b9c1211c7f 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -319,12 +319,18 @@ class ResultCache(SearchQueryParser): matches.add(item[0]) return matches - def get_matches(self, location, query): + def get_matches(self, location, query, allow_recursion=True): matches = set([]) if query and query.strip(): # get metadata key associated with the search term. Eliminates # dealing with plurals and other aliases location = self.field_metadata.search_term_to_key(location.lower().strip()) + if isinstance(location, list): + if allow_recursion: + for loc in location: + matches |= self.get_matches(loc, query, allow_recursion=False) + return matches + raise ParseException(query, len(query), 'Recursive query group detected', self) # take care of dates special case if location in self.field_metadata and \ diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 02141b8f91..91eafbaff0 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -297,6 +297,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) + gst = tweaks['grouped_search_terms'] + for t in gst: + try: + self.field_metadata._add_search_terms_to_map(gst[t], [t]) + except ValueError: + traceback.print_exc() + self.book_on_device_func = None self.data = ResultCache(self.FIELD_MAP, self.field_metadata) self.search = self.data.search diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index 2a79c3a75b..e28b6d422a 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -475,9 +475,7 @@ class FieldMetadata(dict): # ]) def get_search_terms(self): - s_keys = [] - for v in self._tb_cats.itervalues(): - map((lambda x:s_keys.append(x)), v['search_terms']) + s_keys = sorted(self._search_term_map.keys()) for v in self.search_items: s_keys.append(v) # if set(s_keys) != self.DEFAULT_LOCATIONS: @@ -488,6 +486,9 @@ class FieldMetadata(dict): def _add_search_terms_to_map(self, key, terms): if terms is not None: for t in terms: + t = t.lower() + if t in self._search_term_map: + raise ValueError('Attempt to add duplicate search term "%s"'%t) self._search_term_map[t] = key def search_term_to_key(self, term):