From 6d02a8161aee4531f46b4acccdfbc9f80ee6348c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 20 Mar 2026 12:36:17 +0530 Subject: [PATCH] FTS dialog: Allow sorting by any column --- src/calibre/gui2/actions/sort.py | 19 ++++++++++------ src/calibre/gui2/fts/search.py | 37 +++++++++++--------------------- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/calibre/gui2/actions/sort.py b/src/calibre/gui2/actions/sort.py index 85ca57eea3..56bff7cea1 100644 --- a/src/calibre/gui2/actions/sort.py +++ b/src/calibre/gui2/actions/sort.py @@ -30,6 +30,16 @@ from calibre.utils.icu import primary_sort_key SORT_HIDDEN_PREF = 'sort-action-hidden-fields' +def hidden_fields(db): + return frozenset(db.new_api.pref(SORT_HIDDEN_PREF, default=()) or ()) + + +def get_sorted_fields(db): + fm = db.field_metadata + name_map = [(v,k) for k, v in fm.ui_sortable_field_keys().items()] + return sorted(name_map, key=lambda x: primary_sort_key(x[0])) + + class SortAction(QAction): sort_requested = pyqtSignal(object, object) @@ -108,10 +118,7 @@ class SortByAction(InterfaceAction): self.update_menu() def get_sorted_fields(self): - db = self.gui.current_db - fm = db.field_metadata - name_map = [(v,k) for k, v in fm.ui_sortable_field_keys().items()] - return sorted(name_map, key=lambda x: primary_sort_key(x[0])) + return get_sorted_fields(self.gui.current_db) def update_menu(self, menu=None): menu = menu or self.qaction.menu() @@ -149,7 +156,7 @@ class SortByAction(InterfaceAction): menu.addSeparator() # Add the columns to the menu - hidden = frozenset(db.new_api.pref(SORT_HIDDEN_PREF, default=()) or ()) + hidden = hidden_fields(self.gui.current_db) for name, key in self.get_sorted_fields(): if key == 'ondevice' and self.gui.device_connected is None: @@ -164,7 +171,7 @@ class SortByAction(InterfaceAction): def select_sortable_columns(self): db = self.gui.current_db - hidden = frozenset(db.new_api.pref(SORT_HIDDEN_PREF, default=()) or ()) + hidden = hidden_fields(db) items = QListWidget() items.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection) for display_name, key in self.get_sorted_fields(): diff --git a/src/calibre/gui2/fts/search.py b/src/calibre/gui2/fts/search.py index bfb31fc50f..5055a5f188 100644 --- a/src/calibre/gui2/fts/search.py +++ b/src/calibre/gui2/fts/search.py @@ -49,6 +49,7 @@ from calibre import fit_image, prepare_string_for_xml from calibre.db import FTSQueryError from calibre.ebooks.metadata import authors_to_string, fmt_sidx from calibre.gui2 import config, error_dialog, gprefs, info_dialog, question_dialog +from calibre.gui2.actions.sort import get_sorted_fields, hidden_fields from calibre.gui2.fts.cards import CardsView from calibre.gui2.fts.utils import fts_url, get_db, help_panel, jump_shortcut, mark_shortcut, markup_text from calibre.gui2.library.models import render_pin @@ -350,21 +351,11 @@ class ResultsModel(QAbstractItemModel): return False def do_sort_on_field(self, sort_key): - field_map = { - 'newest': [('timestamp', False)], - 'rating': [('rating', False)], - 'pubdate': [('pubdate', False)], - 'size': [('size', False)], - 'pages': [('pages', False)], - } - fields = field_map.get(sort_key) - if fields is None: - return db = get_db() if sort_key == 'pages': db.queue_pages_scan() current_ids = frozenset(r.book_id for r in self.results) - sorted_ids = db.multisort(fields, ids_to_sort=current_ids) + sorted_ids = db.multisort([(sort_key, False)], ids_to_sort=current_ids) results_by_id = {r.book_id: r for r in self.results} self.results = [results_by_id[bid] for bid in sorted_ids if bid in results_by_id] self.result_map = {r.book_id: i for i, r in enumerate(self.results)} @@ -679,24 +670,20 @@ class SearchInputPanel(QWidget): ag = QActionGroup(b) ag.setExclusive(True) current_sort = gprefs['fts_sort_order'] - self._sort_action_map = {} - self._sort_labels = { - 'relevance': _('Relevance'), - 'newest': _('Newest'), - 'rating': _('Highest rated'), - 'pubdate': _('Recently published'), - 'size': _('Largest'), - } - db = get_db() - if db.pref('full_page_scan_requested'): - self._sort_labels['pages'] = _('Most pages') - for key, label in self._sort_labels.items(): + hidden = hidden_fields(get_db()) + self._sort_labels = {} + def add(key, label): + self._sort_labels[key] = label ac = sort_menu.addAction(label) ac.setCheckable(True) ac.setChecked(key == current_sort) ac.setData(key) ag.addAction(ac) - self._sort_action_map[key] = ac + add('relevance', _('Relevance')) + for name, key in get_sorted_fields(get_db()): + if key in hidden or key == 'ondevice': + continue + add(key, name) sort_menu.triggered.connect(self._sort_triggered) self._update_sort_button_label() @@ -1156,7 +1143,7 @@ class ResultsPanel(QWidget): query=query, fts_url=fts_url), det_msg=err_msg, show=True) -def develop(view='compact'): +def develop(view='cards'): from calibre.gui2 import Application from calibre.library import db app = Application([])