Implement restrict to fields UI

This commit is contained in:
Kovid Goyal 2023-09-25 16:37:33 +05:30
parent 5d63fe7e27
commit b88c0551ad
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 82 additions and 7 deletions

View File

@ -691,8 +691,10 @@ class Cache:
return self.backend.get_all_items_that_have_notes(field_name) return self.backend.get_all_items_that_have_notes(field_name)
@read_api @read_api
def field_supports_notes(self, field) -> bool: def field_supports_notes(self, field=None) -> bool:
' Return True iff the specified field supports notes ' ' Return True iff the specified field supports notes. If field is None return frozenset of all fields that support notes. '
if field is None:
return self.backend.notes.allowed_fields
return field in self.backend.notes.allowed_fields return field in self.backend.notes.allowed_fields
@write_api @write_api

View File

@ -96,6 +96,7 @@ class Notes:
f" DELETE FROM notes WHERE colname = '{table.name}' AND item = OLD.id;\n" f" DELETE FROM notes WHERE colname = '{table.name}' AND item = OLD.id;\n"
'END;' 'END;'
) )
self.allowed_fields = frozenset(self.allowed_fields)
SchemaUpgrade(conn, '\n'.join(triggers)) SchemaUpgrade(conn, '\n'.join(triggers))
def delete_field(self, conn, field_name): def delete_field(self, conn, field_name):

View File

@ -2,23 +2,90 @@
# License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net> # License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>
import os import os
from functools import partial
from qt.core import ( from qt.core import (
QCheckBox, QDialogButtonBox, QHBoxLayout, QIcon, QSize, Qt, QToolButton, QCheckBox, QDialogButtonBox, QHBoxLayout, QIcon, QLabel, QMenu, QSize, Qt,
QVBoxLayout, QWidget, pyqtSignal, QToolButton, QVBoxLayout, QWidget, pyqtSignal,
) )
from calibre.db.cache import Cache
from calibre.gui2 import Application, gprefs from calibre.gui2 import Application, gprefs
from calibre.gui2.viewer.widgets import SearchBox from calibre.gui2.viewer.widgets import SearchBox
from calibre.gui2.widgets2 import Dialog from calibre.gui2.widgets2 import Dialog, FlowLayout
def current_db(): def current_db() -> Cache:
from calibre.gui2.ui import get_gui from calibre.gui2.ui import get_gui
return (getattr(current_db, 'ans', None) or get_gui().current_db).new_api return (getattr(current_db, 'ans', None) or get_gui().current_db).new_api
class RestrictFields(QWidget): class RestrictFields(QWidget):
pass
def __init__(self, parent=None):
super().__init__(parent)
self.l = l = FlowLayout(self)
l.setContentsMargins(0, 0, 0, 0)
self.restrict_label = QLabel(_('Restrict to:'))
self.restricted_fields = []
self.add_button = b = QToolButton(self)
b.setIcon(QIcon.ic('plus.png')), b.setText(_('Add')), b.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
b.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
self.fields_menu = m = QMenu()
b.setMenu(m)
m.aboutToShow.connect(self.build_add_menu)
self.remove_button = b = QToolButton(self)
b.setIcon(QIcon.ic('minus.png')), b.setText(_('Remove')), b.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
b.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
self.remove_fields_menu = m = QMenu()
b.setMenu(m)
m.aboutToShow.connect(self.build_remove_menu)
db = current_db()
fm = db.field_metadata
def field_name(field):
return fm[field].get('name') or field
self.field_names = {f:field_name(f) for f in db.field_supports_notes()}
self.field_labels = {f: QLabel(self.field_names[f], self) for f in sorted(self.field_names, key=self.field_names.get)}
for l in self.field_labels.values():
l.setVisible(False)
self.relayout()
def relayout(self):
for i in range(self.l.count()):
self.l.removeItem(self.l.itemAt(i))
for l in self.field_labels.values():
l.setVisible(False)
self.l.addWidget(self.restrict_label)
self.l.addWidget(self.add_button)
for field in self.restricted_fields:
w = self.field_labels[field]
w.setVisible(True)
self.l.addWidget(w)
self.l.addWidget(self.remove_button)
self.remove_button.setVisible(bool(self.restricted_fields))
def build_add_menu(self):
m = self.fields_menu
m.clear()
for field in self.field_labels:
if field not in self.restricted_fields:
m.addAction(self.field_names[field], partial(self.add_field, field))
def build_remove_menu(self):
m = self.remove_fields_menu
m.clear()
for field in self.restricted_fields:
m.addAction(self.field_names[field], partial(self.remove_field, field))
def add_field(self, field):
self.restricted_fields.append(field)
self.relayout()
def remove_field(self, field):
self.restricted_fields.remove(field)
self.relayout()
class SearchInput(QWidget): class SearchInput(QWidget):
@ -54,10 +121,15 @@ class SearchInput(QWidget):
nb.clicked.connect(self.show_previous) nb.clicked.connect(self.show_previous)
nb.setToolTip(_('Find previous match')) nb.setToolTip(_('Find previous match'))
self.restrict = r = RestrictFields(self)
l.addWidget(r)
@property @property
def current_query(self): def current_query(self):
return { return {
'query': self.search_box.lineEdit().text().strip(), 'query': self.search_box.lineEdit().text().strip(),
'restrict_to_fields': tuple(self.restrict.restricted_fields),
} }
def cleared(self): def cleared(self):