From fcdb50df3a915a2b100b6d0e5c102fecf56fed12 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Tue, 24 Jun 2025 13:55:11 +0100 Subject: [PATCH] Add 'Search "not in"' and 'Filter "not in'" buttons to Manage authors and Manage Items. Also clean up tooltips to use consistent language. --- .../gui2/dialogs/edit_authors_dialog.py | 18 ++++--- .../gui2/dialogs/edit_authors_dialog.ui | 40 +++++++++++++--- src/calibre/gui2/dialogs/tag_list_editor.py | 16 ++++--- src/calibre/gui2/dialogs/tag_list_editor.ui | 48 +++++++++++++++++-- 4 files changed, 100 insertions(+), 22 deletions(-) diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.py b/src/calibre/gui2/dialogs/edit_authors_dialog.py index f7002ef781..159dd86230 100644 --- a/src/calibre/gui2/dialogs/edit_authors_dialog.py +++ b/src/calibre/gui2/dialogs/edit_authors_dialog.py @@ -185,8 +185,9 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): ac.triggered.connect(self.clear_find) le.returnPressed.connect(self.do_find) self.find_box.editTextChanged.connect(self.find_text_changed) - self.find_button.clicked.connect(self.do_find) + self.find_button.clicked.connect(partial(self.do_find, inverted=False)) self.find_button.setDefault(True) + self.find_inverted_button.clicked.connect(partial(self.do_find, inverted=True)) self.filter_box.initialize('manage_authors_filter') le = self.filter_box.lineEdit() @@ -194,7 +195,8 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): if ac is not None: ac.triggered.connect(self.clear_filter) self.filter_box.lineEdit().returnPressed.connect(self.do_filter) - self.filter_button.clicked.connect(self.do_filter) + self.filter_button.clicked.connect(partial(self.do_filter, inverted=False)) + self.filter_inverted_button.clicked.connect(partial(self.do_filter, inverted=True)) self.not_found_label = l = QLabel(self.table) l.setFrameStyle(QFrame.Shape.StyledPanel) @@ -212,6 +214,8 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.table.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.table.customContextMenuRequested.connect(self.show_context_menu) + self.inverted_filter = False + # Fetch the data self.authors = {} self.original_authors = {} @@ -274,7 +278,8 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): self.filter_box.setText('') self.show_table(None, None, None, False) - def do_filter(self): + def do_filter(self, inverted): + self.inverted_filter = inverted self.show_table(None, None, None, False) def show_table(self, id_to_select, select_sort, select_link, is_first_letter): @@ -282,7 +287,8 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): filter_text = icu_lower(str(self.filter_box.text())) if filter_text: auts_to_show = {id_ for id_ in auts_to_show - if self.string_contains(filter_text, icu_lower(self.authors[id_]['name']))} + if self.string_contains(filter_text, + icu_lower(self.authors[id_]['name'])) != self.inverted_filter} self.table.blockSignals(True) self.table.clear() @@ -530,7 +536,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): def find_text_changed(self): self.start_find_pos = -1 - def do_find(self): + def do_find(self, inverted=False): self.not_found_label.setVisible(False) # For some reason the button box keeps stealing the RETURN shortcut. # Steal it back @@ -548,7 +554,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog): c = self.start_find_pos % 2 item = self.table.item(r, c) text = icu_lower(str(item.text())) - if st in text: + if (st in text) != inverted: self.table.setCurrentItem(item) self.table.setFocus(Qt.FocusReason.OtherFocusReason) return diff --git a/src/calibre/gui2/dialogs/edit_authors_dialog.ui b/src/calibre/gui2/dialogs/edit_authors_dialog.ui index 5746456f99..1f308cb70e 100644 --- a/src/calibre/gui2/dialogs/edit_authors_dialog.ui +++ b/src/calibre/gui2/dialogs/edit_authors_dialog.ui @@ -37,10 +37,9 @@ - <p>Only show authors that contain the text in this box. - The match ignores case.</p> + Filter names in the Authors column using the text in this box. The search ignores case. - + true @@ -63,13 +62,16 @@ 0 + + Search for names in the Authors column using the text in this box. The search ignores case. + 200 0 - + true @@ -89,6 +91,19 @@ Fi&lter + + Show all authors containing the filter text + + + + + + + Filter "not in" + + + Show all authors that do not contain the filter text + @@ -96,9 +111,22 @@ S&earch + + Search for authors containing the search text + - + + + + Search "not in" + + + Search for authors that do not contain the search text + + + + @@ -126,7 +154,7 @@ - <p>Show items only if they appear in the + <p>Show authors only if they appear in the currently selected books. Edits already done may be hidden but will not be forgotten. </p><p> diff --git a/src/calibre/gui2/dialogs/tag_list_editor.py b/src/calibre/gui2/dialogs/tag_list_editor.py index ef3458de1f..80c09876af 100644 --- a/src/calibre/gui2/dialogs/tag_list_editor.py +++ b/src/calibre/gui2/dialogs/tag_list_editor.py @@ -405,8 +405,9 @@ class TagListEditor(QDialog, Ui_TagListEditor): if ac is not None: ac.triggered.connect(self.clear_search) self.search_box.textChanged.connect(self.search_text_changed) - self.search_button.clicked.connect(self.do_search) + self.search_button.clicked.connect(partial(self.do_search, inverted=False)) self.search_button.setDefault(True) + self.search_inverted_button.clicked.connect(partial(self.do_search, inverted=True)) self.filter_box.initialize('tag_list_filter_box_' + cat_name) le = self.filter_box.lineEdit() @@ -414,7 +415,9 @@ class TagListEditor(QDialog, Ui_TagListEditor): if ac is not None: ac.triggered.connect(self.clear_filter) le.returnPressed.connect(self.do_filter) - self.filter_button.clicked.connect(self.do_filter) + self.filter_button.clicked.connect(partial(self.do_filter, inverted=False)) + self.filter_inverted_button.clicked.connect(partial(self.do_filter, inverted=True)) + self.filter_inverted = False self.show_button_layout.setSpacing(0) self.show_button_layout.setContentsMargins(0, 0, 0, 0) self.apply_all_checkbox.setContentsMargins(0, 0, 0, 0) @@ -600,14 +603,14 @@ class TagListEditor(QDialog, Ui_TagListEditor): return 'virtual_library' return None - def do_search(self): + def do_search(self, inverted=False): self.not_found_label.setVisible(False) find_text = str(self.search_box.currentText()) if not find_text: return for _ in range(self.table.rowCount()): r = self.search_item_row = (self.search_item_row + 1) % self.table.rowCount() - if self.string_contains(find_text, self.table.item(r, VALUE_COLUMN).text()): + if self.string_contains(find_text, self.table.item(r, VALUE_COLUMN).text()) != inverted: self.table.setCurrentItem(self.table.item(r, VALUE_COLUMN)) self.table.setFocus(Qt.FocusReason.OtherFocusReason) return @@ -717,7 +720,7 @@ class TagListEditor(QDialog, Ui_TagListEditor): self.all_tags = {} filter_text = icu_lower(str(self.filter_box.text())) for k,v,count in data: - if not filter_text or self.string_contains(filter_text, icu_lower(v)): + if not filter_text or self.string_contains(filter_text, icu_lower(v)) != self.filter_inverted: self.all_tags[v] = {'key': k, 'count': count, 'cur_name': v, 'is_deleted': k in self.to_delete} self.original_names[k] = v @@ -844,7 +847,8 @@ class TagListEditor(QDialog, Ui_TagListEditor): self.filter_box.setText(txt) self.do_filter() - def do_filter(self): + def do_filter(self, inverted=False): + self.filter_inverted = inverted self.fill_in_table(None, None, False) def table_column_resized(self, *args): diff --git a/src/calibre/gui2/dialogs/tag_list_editor.ui b/src/calibre/gui2/dialogs/tag_list_editor.ui index 5fabb9fa94..bb84d4cc9b 100644 --- a/src/calibre/gui2/dialogs/tag_list_editor.ui +++ b/src/calibre/gui2/dialogs/tag_list_editor.ui @@ -65,7 +65,7 @@ - Search for an item in the first column + Search for an item in the first column using the text in this box. The search ignores case. true @@ -81,7 +81,7 @@ - Find items containing the search string + Search for items that contain the search text S&earch @@ -92,6 +92,26 @@ + + + + + 0 + 0 + + + + Search "not in" + + + Search for items that do not contain the search text + + + + :/images/search.png:/images/search.png + + + @@ -135,7 +155,7 @@ - Filter items using the text in this box + Filter items in the first column using the text in this box. The search ignores case. true @@ -151,7 +171,7 @@ - Show only items containing this text + Show all items that contain the filter text F&ilter @@ -162,6 +182,26 @@ + + + + + 0 + 0 + + + + Filter "not in" + + + Show all items that do not contain the filter text + + + + :/images/filter.png:/images/filter.png + + +