mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add an option in Preferences->Searching to make searching case-sensitive
This commit is contained in:
parent
6f3c8109d4
commit
470f621748
@ -26,7 +26,7 @@ REGEXP_MATCH = 2
|
|||||||
|
|
||||||
# Utils {{{
|
# Utils {{{
|
||||||
|
|
||||||
def _matchkind(query):
|
def _matchkind(query, case_sensitive=False):
|
||||||
matchkind = CONTAINS_MATCH
|
matchkind = CONTAINS_MATCH
|
||||||
if (len(query) > 1):
|
if (len(query) > 1):
|
||||||
if query.startswith('\\'):
|
if query.startswith('\\'):
|
||||||
@ -38,12 +38,12 @@ def _matchkind(query):
|
|||||||
matchkind = REGEXP_MATCH
|
matchkind = REGEXP_MATCH
|
||||||
query = query[1:]
|
query = query[1:]
|
||||||
|
|
||||||
if matchkind != REGEXP_MATCH:
|
if not case_sensitive and matchkind != REGEXP_MATCH:
|
||||||
# leave case in regexps because it can be significant e.g. \S \W \D
|
# leave case in regexps because it can be significant e.g. \S \W \D
|
||||||
query = icu_lower(query)
|
query = icu_lower(query)
|
||||||
return matchkind, query
|
return matchkind, query
|
||||||
|
|
||||||
def _match(query, value, matchkind, use_primary_find_in_search=True):
|
def _match(query, value, matchkind, use_primary_find_in_search=True, case_sensitive=False):
|
||||||
if query.startswith('..'):
|
if query.startswith('..'):
|
||||||
query = query[1:]
|
query = query[1:]
|
||||||
sq = query[1:]
|
sq = query[1:]
|
||||||
@ -52,7 +52,8 @@ def _match(query, value, matchkind, use_primary_find_in_search=True):
|
|||||||
internal_match_ok = False
|
internal_match_ok = False
|
||||||
for t in value:
|
for t in value:
|
||||||
try: # ignore regexp exceptions, required because search-ahead tries before typing is finished
|
try: # ignore regexp exceptions, required because search-ahead tries before typing is finished
|
||||||
t = icu_lower(t)
|
if not case_sensitive:
|
||||||
|
t = icu_lower(t)
|
||||||
if (matchkind == EQUALS_MATCH):
|
if (matchkind == EQUALS_MATCH):
|
||||||
if internal_match_ok:
|
if internal_match_ok:
|
||||||
if query == t:
|
if query == t:
|
||||||
@ -69,10 +70,10 @@ def _match(query, value, matchkind, use_primary_find_in_search=True):
|
|||||||
elif query == t:
|
elif query == t:
|
||||||
return True
|
return True
|
||||||
elif matchkind == REGEXP_MATCH:
|
elif matchkind == REGEXP_MATCH:
|
||||||
if re.search(query, t, re.I|re.UNICODE):
|
if re.search(query, t, re.UNICODE if case_sensitive else re.I|re.UNICODE):
|
||||||
return True
|
return True
|
||||||
elif matchkind == CONTAINS_MATCH:
|
elif matchkind == CONTAINS_MATCH:
|
||||||
if use_primary_find_in_search:
|
if not case_sensitive and use_primary_find_in_search:
|
||||||
if primary_contains(query, t):
|
if primary_contains(query, t):
|
||||||
return True
|
return True
|
||||||
elif query in t:
|
elif query in t:
|
||||||
@ -598,7 +599,8 @@ class Parser(SearchQueryParser): # {{{
|
|||||||
return self.get_user_category_matches(location[1:], icu_lower(query), candidates)
|
return self.get_user_category_matches(location[1:], icu_lower(query), candidates)
|
||||||
|
|
||||||
# Everything else (and 'all' matches)
|
# Everything else (and 'all' matches)
|
||||||
matchkind, query = _matchkind(query)
|
case_sensitive = prefs['case_sensitive']
|
||||||
|
matchkind, query = _matchkind(query, case_sensitive=case_sensitive)
|
||||||
all_locs = set()
|
all_locs = set()
|
||||||
text_fields = set()
|
text_fields = set()
|
||||||
field_metadata = {}
|
field_metadata = {}
|
||||||
@ -644,12 +646,12 @@ class Parser(SearchQueryParser): # {{{
|
|||||||
rm = {v.lower():k for k,v in lm.iteritems()}
|
rm = {v.lower():k for k,v in lm.iteritems()}
|
||||||
q = rm.get(query, query)
|
q = rm.get(query, query)
|
||||||
|
|
||||||
if matchkind == CONTAINS_MATCH and q in {'true', 'false'}:
|
if matchkind == CONTAINS_MATCH and q.lower() in {'true', 'false'}:
|
||||||
found = set()
|
found = set()
|
||||||
for val, book_ids in self.field_iter(location, current_candidates):
|
for val, book_ids in self.field_iter(location, current_candidates):
|
||||||
if val and (not hasattr(val, 'strip') or val.strip()):
|
if val and (not hasattr(val, 'strip') or val.strip()):
|
||||||
found |= book_ids
|
found |= book_ids
|
||||||
matches |= (found if q == 'true' else (current_candidates-found))
|
matches |= (found if q.lower() == 'true' else (current_candidates-found))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
dt = field_metadata.get(location, {}).get('datatype', None)
|
dt = field_metadata.get(location, {}).get('datatype', None)
|
||||||
@ -679,14 +681,14 @@ class Parser(SearchQueryParser): # {{{
|
|||||||
if val is not None:
|
if val is not None:
|
||||||
if isinstance(val, basestring):
|
if isinstance(val, basestring):
|
||||||
val = (val,)
|
val = (val,)
|
||||||
if _match(q, val, matchkind, use_primary_find_in_search=upf):
|
if _match(q, val, matchkind, use_primary_find_in_search=upf, case_sensitive=case_sensitive):
|
||||||
matches |= book_ids
|
matches |= book_ids
|
||||||
|
|
||||||
if location == 'series_sort':
|
if location == 'series_sort':
|
||||||
book_lang_map = self.dbcache.fields['languages'].book_value_map
|
book_lang_map = self.dbcache.fields['languages'].book_value_map
|
||||||
for val, book_ids in self.dbcache.fields['series'].iter_searchable_values_for_sort(current_candidates, book_lang_map):
|
for val, book_ids in self.dbcache.fields['series'].iter_searchable_values_for_sort(current_candidates, book_lang_map):
|
||||||
if val is not None:
|
if val is not None:
|
||||||
if _match(q, (val,), matchkind, use_primary_find_in_search=upf):
|
if _match(q, (val,), matchkind, use_primary_find_in_search=upf, case_sensitive=case_sensitive):
|
||||||
matches |= book_ids
|
matches |= book_ids
|
||||||
|
|
||||||
return matches
|
return matches
|
||||||
|
@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
from PyQt5.Qt import QApplication
|
from PyQt5.Qt import QApplication
|
||||||
|
|
||||||
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, \
|
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, \
|
||||||
CommaSeparatedList
|
CommaSeparatedList, AbortCommit
|
||||||
from calibre.gui2.preferences.search_ui import Ui_Form
|
from calibre.gui2.preferences.search_ui import Ui_Form
|
||||||
from calibre.gui2 import config, error_dialog, gprefs
|
from calibre.gui2 import config, error_dialog, gprefs
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
@ -29,6 +29,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
r('show_highlight_toggle_button', gprefs)
|
r('show_highlight_toggle_button', gprefs)
|
||||||
r('limit_search_columns', prefs)
|
r('limit_search_columns', prefs)
|
||||||
r('use_primary_find_in_search', prefs)
|
r('use_primary_find_in_search', prefs)
|
||||||
|
r('case_sensitive', prefs)
|
||||||
r('limit_search_columns_to', prefs, setting=CommaSeparatedList)
|
r('limit_search_columns_to', prefs, setting=CommaSeparatedList)
|
||||||
fl = db.field_metadata.get_search_terms()
|
fl = db.field_metadata.get_search_terms()
|
||||||
self.opt_limit_search_columns_to.update_items_cache(fl)
|
self.opt_limit_search_columns_to.update_items_cache(fl)
|
||||||
@ -101,6 +102,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.muc_changed = False
|
self.muc_changed = False
|
||||||
self.opt_grouped_search_make_user_categories.lineEdit().editingFinished.connect(
|
self.opt_grouped_search_make_user_categories.lineEdit().editingFinished.connect(
|
||||||
self.muc_box_changed)
|
self.muc_box_changed)
|
||||||
|
self.opt_case_sensitive.toggled.connect(self.case_sensitive_toggled)
|
||||||
|
self.case_sensitive_toggled()
|
||||||
|
|
||||||
|
def case_sensitive_toggled(self):
|
||||||
|
if self.opt_case_sensitive.isChecked():
|
||||||
|
self.opt_use_primary_find_in_search.setChecked(False)
|
||||||
|
|
||||||
def set_similar_fields(self, initial=False):
|
def set_similar_fields(self, initial=False):
|
||||||
self.set_similar('similar_authors_search_key', initial=initial)
|
self.set_similar('similar_authors_search_key', initial=initial)
|
||||||
@ -211,6 +218,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.gst_value.blockSignals(False)
|
self.gst_value.blockSignals(False)
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
|
if self.opt_case_sensitive.isChecked() and self.opt_use_primary_find_in_search.isChecked():
|
||||||
|
error_dialog(self, _('Incompatible options'), _(
|
||||||
|
'The option to have un-accented characters match accented characters has no effect'
|
||||||
|
' if you also turn on case-sensitive searching. So only turn on one of those options'), show=True)
|
||||||
|
raise AbortCommit()
|
||||||
if self.gst_changed:
|
if self.gst_changed:
|
||||||
self.db.new_api.set_pref('grouped_search_terms', self.gst)
|
self.db.new_api.set_pref('grouped_search_terms', self.gst)
|
||||||
self.db.field_metadata.add_grouped_search_terms(self.gst)
|
self.db.field_metadata.add_grouped_search_terms(self.gst)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="5" column="0" colspan="2">
|
<item row="7" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>What to search by default</string>
|
<string>What to search by default</string>
|
||||||
@ -63,7 +63,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="0" colspan="2">
|
<item row="10" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox22">
|
<widget class="QGroupBox" name="groupBox22">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>What to search when searching similar books</string>
|
<string>What to search when searching similar books</string>
|
||||||
@ -152,7 +152,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0" colspan="2">
|
<item row="8" column="0" colspan="2">
|
||||||
<widget class="QPushButton" name="clear_history_button">
|
<widget class="QPushButton" name="clear_history_button">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
@ -175,13 +175,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QCheckBox" name="opt_use_primary_find_in_search">
|
|
||||||
<property name="text">
|
|
||||||
<string>Unaccented characters match accented characters</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0" colspan="2">
|
<item row="1" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="opt_highlight_search_matches">
|
<widget class="QCheckBox" name="opt_highlight_search_matches">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -189,7 +182,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0" colspan="2">
|
<item row="9" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Grouped Search Terms</string>
|
<string>Grouped Search Terms</string>
|
||||||
@ -313,6 +306,20 @@ to be shown as user categories</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_case_sensitive">
|
||||||
|
<property name="text">
|
||||||
|
<string>Case sensitive searching</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QCheckBox" name="opt_use_primary_find_in_search">
|
||||||
|
<property name="text">
|
||||||
|
<string>Unaccented characters match accented characters</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
@ -437,7 +437,10 @@ def create_global_prefs(conf_obj=None):
|
|||||||
u' English, searching for n will match %s and n, but if '
|
u' English, searching for n will match %s and n, but if '
|
||||||
'your language is Spanish it will only match n. Note that '
|
'your language is Spanish it will only match n. Note that '
|
||||||
'this is much slower than a simple search on very large '
|
'this is much slower than a simple search on very large '
|
||||||
'libraries.')%u'\xf1')
|
'libraries. Note that this option will have no effect if you turn '
|
||||||
|
'on case-sensitive searching')%u'\xf1')
|
||||||
|
c.add_opt('case_sensitive', default=False, help=_(
|
||||||
|
'Make searches case-sensitive'))
|
||||||
|
|
||||||
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