mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add a 'search limit' feature where the user can specify a set of fields (columns) to search when non-prefixed terms are used
This commit is contained in:
parent
b8b6c83a1d
commit
1d8e98122c
@ -202,6 +202,15 @@ class SearchBar(QWidget): # {{{
|
|||||||
l.addWidget(x)
|
l.addWidget(x)
|
||||||
x.setVisible(False)
|
x.setVisible(False)
|
||||||
|
|
||||||
|
x = parent.search_limit_to = QCheckBox()
|
||||||
|
x.setText(_('&Limit'))
|
||||||
|
x.setToolTip('<p>'+_('When searching for text without using lookup '
|
||||||
|
'prefixes, as for example someword instead of title:someword, '
|
||||||
|
'limit the columns searched to those named in the option '
|
||||||
|
'Preferences -> Look and Feel -> Limit non-prefixed searches to columns.'))
|
||||||
|
x.setVisible(False)
|
||||||
|
l.addWidget(x)
|
||||||
|
|
||||||
x = parent.saved_search = SavedSearchBox(self)
|
x = parent.saved_search = SavedSearchBox(self)
|
||||||
x.setMaximumSize(QSize(150, 16777215))
|
x.setMaximumSize(QSize(150, 16777215))
|
||||||
x.setMinimumContentsLength(15)
|
x.setMinimumContentsLength(15)
|
||||||
|
@ -62,6 +62,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
r('tags_browser_partition_method', gprefs, choices=choices)
|
r('tags_browser_partition_method', gprefs, choices=choices)
|
||||||
r('tags_browser_collapse_at', gprefs)
|
r('tags_browser_collapse_at', gprefs)
|
||||||
|
|
||||||
|
r('search_box_limit_to', prefs)
|
||||||
|
|
||||||
self.current_font = None
|
self.current_font = None
|
||||||
self.change_font_button.clicked.connect(self.change_font)
|
self.change_font_button.clicked.connect(self.change_font)
|
||||||
|
|
||||||
@ -119,6 +121,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
gui.search.search_as_you_type(config['search_as_you_type'])
|
gui.search.search_as_you_type(config['search_as_you_type'])
|
||||||
self.update_font_display()
|
self.update_font_display()
|
||||||
gui.tags_view.reread_collapse_parameters()
|
gui.tags_view.reread_collapse_parameters()
|
||||||
|
gui.search_limit_to.setEnabled(bool(prefs['search_box_limit_to']))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
|
@ -200,6 +200,26 @@ up into sub-categories. If the partition method is set to disable, this value is
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QLabel" name="label_81">
|
||||||
|
<property name="text">
|
||||||
|
<string>Limit non-&prefixed searches to columns:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_search_box_limit_to</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<widget class="QLineEdit" name="opt_search_box_limit_to">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Choose columns to be searched when not using prefixes, as for
|
||||||
|
example when searching for someword instead of title:someword.
|
||||||
|
Enter a list of search/lookup names separated by commas. You
|
||||||
|
must check the 'Limit' box on the GUI for this option to take effect.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="15" column="0" colspan="2">
|
<item row="15" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
@ -16,7 +16,7 @@ from calibre.gui2 import config
|
|||||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||||
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
|
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
|
||||||
from calibre.gui2.dialogs.search import SearchDialog
|
from calibre.gui2.dialogs.search import SearchDialog
|
||||||
from calibre.utils.config import dynamic
|
from calibre.utils.config import dynamic, prefs
|
||||||
from calibre.utils.search_query_parser import saved_searches
|
from calibre.utils.search_query_parser import saved_searches
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
|
|
||||||
@ -379,6 +379,12 @@ class SearchBoxMixin(object): # {{{
|
|||||||
self.search_highlight_only.stateChanged.connect(self.highlight_only_changed)
|
self.search_highlight_only.stateChanged.connect(self.highlight_only_changed)
|
||||||
self.search_highlight_only.setChecked(
|
self.search_highlight_only.setChecked(
|
||||||
dynamic.get('search_highlight_only', False))
|
dynamic.get('search_highlight_only', False))
|
||||||
|
self.search_limit_to.stateChanged.connect(self.search_limit_to_changed)
|
||||||
|
self.search_limit_to.setVisible(True)
|
||||||
|
chk = dynamic.get('use_search_box_limit', False)
|
||||||
|
self.search_limit_to.setChecked(chk)
|
||||||
|
prefs['use_search_box_limit'] = chk
|
||||||
|
self.search_limit_to.setEnabled(bool(prefs['search_box_limit_to']))
|
||||||
|
|
||||||
def focus_search_box(self, *args):
|
def focus_search_box(self, *args):
|
||||||
self.search.setFocus(Qt.OtherFocusReason)
|
self.search.setFocus(Qt.OtherFocusReason)
|
||||||
@ -410,6 +416,11 @@ class SearchBoxMixin(object): # {{{
|
|||||||
self.current_view().model().set_highlight_only(toWhat)
|
self.current_view().model().set_highlight_only(toWhat)
|
||||||
self.focus_to_library()
|
self.focus_to_library()
|
||||||
|
|
||||||
|
def search_limit_to_changed(self, toWhat):
|
||||||
|
dynamic.set('use_search_box_limit', toWhat)
|
||||||
|
prefs['use_search_box_limit'] = toWhat
|
||||||
|
self.search.do_search()
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class SavedSearchBoxMixin(object): # {{{
|
class SavedSearchBoxMixin(object): # {{{
|
||||||
|
@ -1214,7 +1214,7 @@ class TagBrowserMixin(object): # {{{
|
|||||||
db.field_metadata.remove_user_categories()
|
db.field_metadata.remove_user_categories()
|
||||||
for k in d.categories:
|
for k in d.categories:
|
||||||
db.field_metadata.add_user_category('@' + k, k)
|
db.field_metadata.add_user_category('@' + k, k)
|
||||||
db.data.sqp_change_locations(db.field_metadata.get_search_terms())
|
db.data.change_search_locations(db.field_metadata.get_search_terms())
|
||||||
self.tags_view.set_new_model()
|
self.tags_view.set_new_model()
|
||||||
self.tags_view.recount()
|
self.tags_view.recount()
|
||||||
|
|
||||||
|
@ -482,8 +482,10 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
for action in self.iactions.values():
|
for action in self.iactions.values():
|
||||||
action.location_selected(location)
|
action.location_selected(location)
|
||||||
if location == 'library':
|
if location == 'library':
|
||||||
|
self.search_limit_to.setVisible(True)
|
||||||
self.search_restriction.setEnabled(True)
|
self.search_restriction.setEnabled(True)
|
||||||
else:
|
else:
|
||||||
|
self.search_limit_to.setVisible(False)
|
||||||
self.search_restriction.setEnabled(False)
|
self.search_restriction.setEnabled(False)
|
||||||
# Reset the view in case something changed while it was invisible
|
# Reset the view in case something changed while it was invisible
|
||||||
self.current_view().reset()
|
self.current_view().reset()
|
||||||
|
@ -11,7 +11,7 @@ from itertools import repeat
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from calibre.utils.config import tweaks
|
from calibre.utils.config import tweaks, prefs
|
||||||
from calibre.utils.date import parse_date, now, UNDEFINED_DATE
|
from calibre.utils.date import parse_date, now, UNDEFINED_DATE
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
from calibre.utils.pyparsing import ParseException
|
from calibre.utils.pyparsing import ParseException
|
||||||
@ -182,15 +182,16 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
self.first_sort = True
|
self.first_sort = True
|
||||||
self.search_restriction = ''
|
self.search_restriction = ''
|
||||||
self.field_metadata = field_metadata
|
self.field_metadata = field_metadata
|
||||||
all_search_locations = field_metadata.get_search_terms()
|
self.all_search_locations = field_metadata.get_search_terms()
|
||||||
SearchQueryParser.__init__(self, all_search_locations, optimize=True)
|
SearchQueryParser.__init__(self, self.all_search_locations, optimize=True)
|
||||||
self.build_date_relop_dict()
|
self.build_date_relop_dict()
|
||||||
self.build_numeric_relop_dict()
|
self.build_numeric_relop_dict()
|
||||||
|
|
||||||
def break_cycles(self):
|
def break_cycles(self):
|
||||||
self._data = self.field_metadata = self.FIELD_MAP = \
|
self._data = self.field_metadata = self.FIELD_MAP = \
|
||||||
self.numeric_search_relops = self.date_search_relops = \
|
self.numeric_search_relops = self.date_search_relops = \
|
||||||
self.db_prefs = None
|
self.db_prefs = self.all_search_locations = None
|
||||||
|
self.sqp_change_locations([])
|
||||||
|
|
||||||
|
|
||||||
def __getitem__(self, row):
|
def __getitem__(self, row):
|
||||||
@ -218,6 +219,10 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
def universal_set(self):
|
def universal_set(self):
|
||||||
return set([i[0] for i in self._data if i is not None])
|
return set([i[0] for i in self._data if i is not None])
|
||||||
|
|
||||||
|
def change_search_locations(self, locations):
|
||||||
|
self.sqp_change_locations(locations)
|
||||||
|
self.all_search_locations = locations
|
||||||
|
|
||||||
def build_date_relop_dict(self):
|
def build_date_relop_dict(self):
|
||||||
'''
|
'''
|
||||||
Because the database dates have time in them, we can't use direct
|
Because the database dates have time in them, we can't use direct
|
||||||
@ -432,6 +437,7 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
# get metadata key associated with the search term. Eliminates
|
# get metadata key associated with the search term. Eliminates
|
||||||
# dealing with plurals and other aliases
|
# dealing with plurals and other aliases
|
||||||
location = self.field_metadata.search_term_to_field_key(icu_lower(location.strip()))
|
location = self.field_metadata.search_term_to_field_key(icu_lower(location.strip()))
|
||||||
|
# grouped search terms
|
||||||
if isinstance(location, list):
|
if isinstance(location, list):
|
||||||
if allow_recursion:
|
if allow_recursion:
|
||||||
for loc in location:
|
for loc in location:
|
||||||
@ -440,6 +446,20 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
return matches
|
return matches
|
||||||
raise ParseException(query, len(query), 'Recursive query group detected', self)
|
raise ParseException(query, len(query), 'Recursive query group detected', self)
|
||||||
|
|
||||||
|
# apply the limit if appropriate
|
||||||
|
if location == 'all' and prefs['use_search_box_limit'] and \
|
||||||
|
prefs['search_box_limit_to']:
|
||||||
|
for l in prefs['search_box_limit_to'].split(','):
|
||||||
|
l = icu_lower(l.strip())
|
||||||
|
if not l or l == 'all':
|
||||||
|
continue
|
||||||
|
if l not in self.all_search_locations:
|
||||||
|
raise ParseException(l, len(l),
|
||||||
|
'Unknown field "%s" in search column limit'%l, self)
|
||||||
|
matches |= self.get_matches(l, query,
|
||||||
|
candidates=candidates, allow_recursion=allow_recursion)
|
||||||
|
return matches
|
||||||
|
|
||||||
if location in self.field_metadata:
|
if location in self.field_metadata:
|
||||||
fm = self.field_metadata[location]
|
fm = self.field_metadata[location]
|
||||||
# take care of dates special case
|
# take care of dates special case
|
||||||
|
@ -729,6 +729,11 @@ def _prefs():
|
|||||||
c.add_opt('manage_device_metadata', default='manual',
|
c.add_opt('manage_device_metadata', default='manual',
|
||||||
help=_('How and when calibre updates metadata on the device.'))
|
help=_('How and when calibre updates metadata on the device.'))
|
||||||
|
|
||||||
|
c.add_opt('search_box_limit_to', default='',
|
||||||
|
help=_('Comma-separated list of fields to search when no prefix'))
|
||||||
|
c.add_opt('use_search_box_limit', default=False,
|
||||||
|
help=_('Set to true to apply the search box limit'))
|
||||||
|
|
||||||
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
|
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user