From 616290b17e983e71962f4ef5a7bba67e8cf82e7d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 20 Sep 2022 07:25:58 +0530 Subject: [PATCH] Cleanup previous PR Tag browser: When searching the Tag browser allow also filtering the book list to show only books that match one of the categories currently shown in the Tag browser. To use enable the two Preferences: Preferences->Look & feel->Tag browser->Hide empty categories and Find shows all items that match. Then when searching the Tag browser, press Ctrl+Alt+Shift+F to restrict the displayed books. Fixes #1989813 --- manual/gui.rst | 17 +++++++++++++++-- src/calibre/db/view.py | 13 +++++++++---- src/calibre/gui2/tag_browser/model.py | 3 +-- src/calibre/gui2/tag_browser/ui.py | 2 +- src/calibre/library/caches.py | 16 ++++++---------- 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/manual/gui.rst b/manual/gui.rst index c4360d2d8d..96983a58b3 100644 --- a/manual/gui.rst +++ b/manual/gui.rst @@ -526,9 +526,19 @@ Identifiers (e.g., ISBN, DOI, LCCN, etc.) use an extended syntax. An identifier * ``identifiers:=isbn:=123456789`` will find books with a type equal to ISBN having a value equal to `123456789`. * ``identifiers:i:1`` will find books with a type containing an `i` having a value containing a `1`. -*Categories in the Tag browser* +*Categories visible in the Tag browser* -The search ``in_tag_browser:true`` finds all books with items (values in categories) currently shown in the :guilabel:`Tag browser`. This is useful if you set the preferences :guilabel:`Preferences . Look & feel . Tag browser . Hide empty categories` and :guilabel:`. Find shows all items that match`. With those two preferences set, doing a ``find`` in the :guilabel:`Tag browser` shows only categories containing items matched by the ``find``. The search ``in_tag_browser:true`` finds books with these categories / items. +The search ``in_tag_browser:true`` finds all books that are in categories +(tags, authors, etc.) currently shown in the :guilabel:`Tag browser`. This is +useful if you set the two preferences :guilabel:`Preferences->Look & feel->Tag +browser->Hide empty categories` and :guilabel:`Find shows all items that +match`. With those two preferences set, doing a :guilabel:`Find` in the +:guilabel:`Tag browser` shows only categories containing items matched by the +:guilabel:`Find`. Then, the search ``in_tag_browser:true`` additionally finds books +with these categories / items. You can easily run this search by pressing the +key :kbd:`Ctrl+Alt+Shift+F` or clicking the configure button in the +:guilabel:`Tag browser` and choosing the :guilabel:`Show only books that have +visible categories` entry. *Search using templates* @@ -906,3 +916,6 @@ calibre has several keyboard shortcuts to save you time and mouse movement. Thes - Quit calibre * - :kbd:`X` - Toggle auto scroll of the book list + * - :kbd:`Ctrl+Alt+Shift+F` + - Restrict the displayed books to only those books that are in a category + currently displayed in the :guilabel:`Tag browser` diff --git a/src/calibre/db/view.py b/src/calibre/db/view.py index 1bbb7b8069..9f854c59a0 100644 --- a/src/calibre/db/view.py +++ b/src/calibre/db/view.py @@ -5,7 +5,7 @@ __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import weakref, operator, numbers +import weakref, operator, numbers, sys from functools import partial from polyglot.builtins import iteritems, itervalues @@ -45,8 +45,13 @@ class InTagBrowserVirtualField: yield str(book_id) if self._ids is None or book_id in self._ids else default_value, {book_id} def sort_keys_for_books(self, get_metadata, lang_map): - def key(_id): - return _id if self._ids is not None and _id in self._ids else '' + null = sys.maxsize + if self._ids is None: + def key(_id): + return null + else: + def key(_id): + return _id if _id in self._ids else null return key @@ -95,7 +100,7 @@ class View: def __init__(self, cache): self.cache = cache self.marked_ids = {} - self.tag_browser_ids = None; + self.tag_browser_ids = None self.marked_listeners = {} self.search_restriction_book_count = 0 self.search_restriction = self.base_restriction = '' diff --git a/src/calibre/gui2/tag_browser/model.py b/src/calibre/gui2/tag_browser/model.py index ea868281ca..dcd8be1af6 100644 --- a/src/calibre/gui2/tag_browser/model.py +++ b/src/calibre/gui2/tag_browser/model.py @@ -818,14 +818,13 @@ class TagsModel(QAbstractItemModel): # {{{ # building the set for a case that will certainly rarely be different # from all books because all books have authors. id_set = set() - for x in [a for a in self.root_item.children if a.category_key != 'search' and not a.is_gst]: + for x in (a for a in self.root_item.children if a.category_key != 'search' and not a.is_gst): for t in x.child_tags(): id_set |= t.tag.id_set changed = self.db.data.get_in_tag_browser() != id_set self.db.data.set_in_tag_browser(id_set) return changed - def get_category_editor_data(self, category): for cat in self.root_item.children: if cat.category_key == category: diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index bc612be407..0d52e5a425 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -734,7 +734,7 @@ class TagBrowserWidget(QFrame): # {{{ mt.m = l.manage_menu = QMenu(l.m) mt.setMenu(mt.m) - l.m.filter_action = ac = l.m.addAction(QIcon.ic('filter.png'), _('Filter book list (search %s)') % 'in_tag_browser:true') + l.m.filter_action = ac = l.m.addAction(QIcon.ic('filter.png'), _('Show only books that have visible categories')) # Give it a (complicated) shortcut so people can discover a shortcut # is possible, I hope without creating collisions. parent.keyboard.register_shortcut('tag browser filter booklist', diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 2bfcb2c564..ebab78c06c 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -9,6 +9,7 @@ import time, traceback, locale from itertools import repeat from datetime import timedelta from threading import Thread +from contextlib import suppress from calibre.utils.config import tweaks, prefs from calibre.utils.date import parse_date, now, UNDEFINED_DATE, clean_date_for_sort @@ -929,18 +930,13 @@ class ResultCache(SearchQueryParser): # {{{ # Set the values in the cache marked_col = self.FIELD_MAP['marked'] - for r in self.iterall(): - r[marked_col] = None - - for id_, val in iteritems(self.marked_ids_dict): - try: - self._data[id_][marked_col] = val - except: - pass - in_tag_browser_col = self.FIELD_MAP['in_tag_browser'] for r in self.iterall(): - r[in_tag_browser_col] = None + r[marked_col] = r[in_tag_browser_col] = None + + for id_, val in self.marked_ids_dict.items(): + with suppress(Exception): + self._data[id_][marked_col] = val def get_marked(self, idx, index_is_id=True, default_value=None): id_ = idx if index_is_id else self[idx][0]