diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index a081926eba..7e5e02b1de 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -150,6 +150,7 @@ def create_defs(): defs['ui_style'] = 'calibre' if iswindows or isosx else 'system' defs['tag_browser_old_look'] = False defs['tag_browser_hide_empty_categories'] = False + defs['tag_browser_always_autocollapse'] = False defs['book_list_tooltips'] = True defs['show_layout_buttons'] = False defs['bd_show_cover'] = True diff --git a/src/calibre/gui2/dialogs/quickview.py b/src/calibre/gui2/dialogs/quickview.py index 48457384ff..5c7b2a8b33 100644 --- a/src/calibre/gui2/dialogs/quickview.py +++ b/src/calibre/gui2/dialogs/quickview.py @@ -281,7 +281,7 @@ class Quickview(QDialog, Ui_Quickview): def item_doubleclicked(self, item): tb = self.gui.stack.tb_widget tb.set_focus_to_find_box() - tb.item_search.lineEdit().setText(self.current_key + ':' + item.text()) + tb.item_search.lineEdit().setText(self.current_key + ':=' + item.text()) tb.do_find() def show_context_menu(self, point): diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 2e2b8e76df..334b76cd65 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -412,6 +412,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('row_numbers_in_book_list', gprefs) r('tag_browser_old_look', gprefs) r('tag_browser_hide_empty_categories', gprefs) + r('tag_browser_always_autocollapse', gprefs) r('tag_browser_show_tooltips', gprefs) r('bd_show_cover', gprefs) r('bd_overlay_cover_size', gprefs) diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index 7ce4cd2449..c758319824 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -995,6 +995,18 @@ then the tags will be displayed each on their own line. + + + + When checked, Find in the Tag browser will show all items + that match the search instead of the first one. If Hide empty categories is + also checked then only categories containing a matched item will be shown. + + + Find &shows all items that match in the Tag browser + + + diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py index 6bb903c3c9..779511ff68 100644 --- a/src/calibre/gui2/tag_browser/model.py +++ b/src/calibre/gui2/tag_browser/model.py @@ -994,9 +994,19 @@ class TagsModel(QAbstractItemModel): # {{{ self.restriction_error.emit() if self.filter_categories_by: + if self.filter_categories_by.startswith('='): + use_exact_match = True + filter_by = self.filter_categories_by[1:] + else: + use_exact_match = False + filter_by = self.filter_categories_by for category in data.keys(): - data[category] = [t for t in data[category] - if lower(t.name).find(self.filter_categories_by) >= 0] + if use_exact_match: + data[category] = [t for t in data[category] + if lower(t.name) == filter_by] + else: + data[category] = [t for t in data[category] + if lower(t.name).find(filter_by) >= 0] # Build a dict of the keys that have data tb_categories = self.db.field_metadata diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index 9359f79fd5..b67f643ac6 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -429,13 +429,16 @@ class TagBrowserBar(QWidget): # {{{ self.item_search.setSizeAdjustPolicy(self.item_search.AdjustToMinimumContentsLengthWithIcon) self.item_search.initialize('tag_browser_search') self.item_search.completer().setCaseSensitivity(Qt.CaseSensitive) - self.item_search.setToolTip(_( - 'Search for items. This is a "contains" search; items containing the\n' - 'text anywhere in the name will be found. You can limit the search\n' - 'to particular categories using syntax similar to search. For example,\n' - 'tags:foo will find foo in any tag, but not in authors etc. Entering\n' - '*foo will filter all categories at once, showing only those items\n' - 'containing the text "foo"')) + self.item_search.setToolTip( + '

' +_( + 'Search for items. If the text begins with equals (=) the search is ' + 'exact match, otherwise it is "contains" finding items containing ' + 'the text anywhere in the item name. Both exact and contains ' + 'searches ignore case. You can limit the search to particular ' + 'categories using syntax similar to search. For example, ' + 'tags:foo will find foo in any tag, but not in authors etc. Entering ' + '*foo will collapse all categories then showing only those categories ' + 'with items containing the text "foo"') + '= 0: + key = self._parent.library_view.model().db.\ + field_metadata.search_term_to_field_key(txt[:colon]) + if key in self._parent.library_view.model().db.field_metadata: + txt = txt[colon+1:] + else: + key = '' + txt = txt[1:] if colon == 0 else txt + else: + key = None + + # key is None indicates that no colon was found. + # key == '' means either a leading : was found or the key is invalid + + # At this point the txt might have a leading =, in which case do an + # exact match search + + if (gprefs.get('tag_browser_always_autocollapse', False) and + key is None and not txt.startswith('*')): + txt = '*' + txt if txt.startswith('*'): self.tags_view.collapseAll() model.set_categories_filter(txt[1:]) @@ -659,18 +686,14 @@ class TagBrowserWidget(QFrame): # {{{ self.search_button.setFocus(True) self.item_search.lineEdit().blockSignals(False) - key = None - colon = txt.find(':') 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]) - if key in self._parent.library_view.model().db.field_metadata: - txt = txt[colon+1:] - else: - key = None - + if txt.startswith('='): + equals_match = True + txt = txt[1:] + else: + equals_match = False self.current_find_position = \ - model.find_item_node(key, txt, self.current_find_position) + model.find_item_node(key, txt, self.current_find_position, + equals_match=equals_match) if self.current_find_position: self.tags_view.show_item_at_path(self.current_find_position, box=True)