diff --git a/src/calibre/gui2/actions/similar_books.py b/src/calibre/gui2/actions/similar_books.py index 6967e70db5..8c7fe1d0ed 100644 --- a/src/calibre/gui2/actions/similar_books.py +++ b/src/calibre/gui2/actions/similar_books.py @@ -26,40 +26,56 @@ class SimilarBooksAction(InterfaceAction): (_('Books in this series'), 'books_in_series.png', 'series', _('Alt+Shift+S')), (_('Books by this publisher'), 'publisher.png', 'publisher', _('Alt+P')), - (_('Books with the same tags'), 'tags.png', 'tag', _('Alt+T')),]: + (_('Books with the same tags'), 'tags.png', 'tags', _('Alt+T')),]: ac = self.create_action(spec=(text, icon, None, shortcut), attr=target) m.addAction(ac) ac.triggered.connect(partial(self.show_similar_books, target)) self.qaction.setMenu(m) - def show_similar_books(self, type, *args): - search, join = [], ' ' + def show_similar_books(self, typ, *args): idx = self.gui.library_view.currentIndex() - db = self.gui.library_view.model().db if not idx.isValid(): return + db = idx.model().db row = idx.row() - if type == 'series': - series = idx.model().db.series(row) - if series: - search = [db.prefs['similar_series_search_key'] + ':"'+series+'"'] - elif type == 'publisher': - publisher = idx.model().db.publisher(row) - if publisher: - search = [db.prefs['similar_publisher_search_key'] + ':"'+publisher+'"'] - elif type == 'tag': - tags = idx.model().db.tags(row) - if tags: - search = [db.prefs['similar_tags_search_key'] + ':"='+t+'"' - for t in tags.split(',')] - elif type in ('author', 'authors'): - authors = idx.model().db.authors(row) - if authors: - search = [db.prefs['similar_authors_search_key'] + - ':"='+a.strip().replace('|', ',')+'"' \ - for a in authors.split(',')] - join = ' or ' + + # Get the parameters for this search + col = db.prefs['similar_' + typ + '_search_key'] + match = db.prefs['similar_' + typ + '_match_kind'] + if match == _('Match all'): + join = ' and ' + else: + join = ' or ' + + # Get all the data for the current record + mi = db.get_metadata(row) + + # Get the definitive field name to use for this search. If the field + # is a grouped search term, the function returns the list of fields that + # are to be searched, otherwise it returns the field name. + loc = db.field_metadata.search_term_to_field_key(icu_lower(col)) + if isinstance(loc, list): + # Grouped search terms are a list of fields. Get all the values, + # pruning duplicates + val = set() + for f in loc: + v = mi.get(f, None) + if not v: + continue + if isinstance(v, list): + val.update(v) + else: + val.add(v) + else: + # Get the value of the requested field. Can be a list or a simple val + val = mi.get(col, None) + if not val: + return + + if not isinstance(val, (list, set)): + val = [val] + search = [col + ':"='+t+'"' for t in val] if search: self.gui.search.set_search_string(join.join(search), store_in_history=True) diff --git a/src/calibre/gui2/preferences/search.py b/src/calibre/gui2/preferences/search.py index 336e30e4ba..ea0cae3ab2 100644 --- a/src/calibre/gui2/preferences/search.py +++ b/src/calibre/gui2/preferences/search.py @@ -71,6 +71,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.gst_value.update_items_cache(fl) self.fill_gst_box(select=None) + self.category_fields = fl + ml = [_('Match any'), _('Match all')] + r('similar_authors_match_kind', db.prefs, choices=ml) + r('similar_tags_match_kind', db.prefs, choices=ml) + r('similar_series_match_kind', db.prefs, choices=ml) + r('similar_publisher_match_kind', db.prefs, choices=ml) self.set_similar_fields(initial=True) self.similar_authors_search_key.currentIndexChanged[int].connect(self.something_changed) self.similar_tags_search_key.currentIndexChanged[int].connect(self.something_changed) @@ -107,7 +113,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): val = self.db.prefs[name] field.blockSignals(True) field.clear() - choices = [first_item] + choices = [] + choices.extend(self.category_fields) choices.extend(sorted(self.gst.keys(), key=sort_key)) field.addItems(choices) dex = field.findText(val) diff --git a/src/calibre/gui2/preferences/search.ui b/src/calibre/gui2/preferences/search.ui index afe212d661..f41a42e864 100644 --- a/src/calibre/gui2/preferences/search.ui +++ b/src/calibre/gui2/preferences/search.ui @@ -207,13 +207,14 @@ to be shown as user categories What to search when searching similar books - + - <p>When you search for similar books by right clicking the book and selecting "Similar books...", + <p>When you search for similar books by right clicking the + book and selecting "Similar books...", calibre constructs a search using the column lookup names defined below. By changing the lookup name to a grouped search term you can - search multiple columns.</p> + search multiple columns at once.</p> true @@ -238,13 +239,17 @@ to be shown as user categories + + + + Similar series: - + @@ -254,6 +259,10 @@ to be shown as user categories + + + + @@ -266,16 +275,24 @@ to be shown as user categories + + + + Similar publishers: - + + + + + diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index dd2a265dba..d3f27e36d4 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -237,9 +237,13 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): defs['column_color_rules'] = [] defs['grouped_search_make_user_categories'] = [] defs['similar_authors_search_key'] = 'author' + defs['similar_authors_match_kind'] = _('Match any') defs['similar_publisher_search_key'] = 'publisher' + defs['similar_publisher_match_kind'] = _('Match any') defs['similar_tags_search_key'] = 'tags' + defs['similar_tags_match_kind'] = _('Match all') defs['similar_series_search_key'] = 'series' + defs['similar_series_match_kind'] = _('Match any') # Migrate the bool tristate tweak defs['bools_are_tristate'] = \