Bug #2002195: Search breaks if non-number used in numeric-sorted column.

This fix is really an enhancement, adding better error presentation to the GUI search box.
This commit is contained in:
Charles Haley 2023-01-08 11:26:46 +00:00
parent 0ebd840d6a
commit ff1952b8b7
3 changed files with 44 additions and 27 deletions

View File

@ -56,36 +56,36 @@ def _match(query, value, matchkind, use_primary_find_in_search=True, case_sensit
else:
internal_match_ok = False
for t in value:
try: # ignore regexp exceptions, required because search-ahead tries before typing is finished
if not case_sensitive:
t = icu_lower(t)
if (matchkind == EQUALS_MATCH):
if internal_match_ok:
if query == t:
return True
return sq in [c.strip() for c in t.split('.') if c.strip()]
elif query[0] == '.':
if t.startswith(query[1:]):
ql = len(query) - 1
if (len(t) == ql) or (t[ql:ql+1] == '.'):
return True
elif query == t:
if not case_sensitive:
t = icu_lower(t)
if (matchkind == EQUALS_MATCH):
if internal_match_ok:
if query == t:
return True
elif matchkind == REGEXP_MATCH:
flags = regex.UNICODE | regex.VERSION1 | regex.FULLCASE | (0 if case_sensitive else regex.IGNORECASE)
return sq in [c.strip() for c in t.split('.') if c.strip()]
elif query[0] == '.':
if t.startswith(query[1:]):
ql = len(query) - 1
if (len(t) == ql) or (t[ql:ql+1] == '.'):
return True
elif query == t:
return True
elif matchkind == REGEXP_MATCH:
flags = regex.UNICODE | regex.VERSION1 | regex.FULLCASE | (0 if case_sensitive else regex.IGNORECASE)
try:
if regex.search(query, t, flags) is not None:
return True
elif matchkind == ACCENT_MATCH:
if primary_contains(query, t):
except regex.error as e:
raise ParseException(_('Invalid regular expression: {}').format(str(e)))
elif matchkind == ACCENT_MATCH:
if primary_contains(query, t):
return True
elif matchkind == CONTAINS_MATCH:
if not case_sensitive and use_primary_find_in_search:
if primary_no_punc_contains(query, t):
return True
elif matchkind == CONTAINS_MATCH:
if not case_sensitive and use_primary_find_in_search:
if primary_no_punc_contains(query, t):
return True
elif query in t:
return True
except regex.error:
pass
elif query in t:
return True
return False
# }}}
@ -298,7 +298,8 @@ class NumericSearch: # {{{
try:
v = cast(val)
except Exception:
v = None
raise ParseException(
_('Non-numeric value in column {0}: {1}').format(location, val))
if v:
v = adjust(v)
if relop(v, q):

View File

@ -242,6 +242,11 @@ class SearchBar(QFrame): # {{{
_('Advanced search'), default_keys=("Shift+Ctrl+F",),
action=ac)
# This error icon will be placed after the clear button icon
parent.search.parse_error_action = ac = parent.search.add_action('dialog_error.png', QLineEdit.ActionPosition.TrailingPosition)
parent.addAction(ac)
ac.setVisible(False)
self.search_button = QToolButton()
self.search_button.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextOnly)
self.search_button.setIcon(QIcon.ic('search.png'))

View File

@ -142,6 +142,7 @@ class SearchBox2(QComboBox): # {{{
self.setMinimumContentsLength(25)
self._in_a_search = False
self.tool_tip_text = self.toolTip()
self.parse_error_action = None
def add_action(self, icon, position=QLineEdit.ActionPosition.TrailingPosition):
if not isinstance(icon, QIcon):
@ -180,6 +181,7 @@ class SearchBox2(QComboBox): # {{{
return self.currentText()
def clear(self, emit_search=True):
self.show_parse_error_action(False)
self.normalize_state()
self.setEditText('')
if emit_search:
@ -191,9 +193,17 @@ class SearchBox2(QComboBox): # {{{
self.clear()
self.setFocus(Qt.FocusReason.OtherFocusReason)
def show_parse_error_action(self, to_show, tooltip=''):
try:
self.parse_error_action.setVisible(to_show)
self.parse_error_action.setToolTip(tooltip)
except Exception:
pass
def search_done(self, ok):
if isinstance(ok, string_or_bytes):
self.setToolTip(ok)
self.show_parse_error_action(True, tooltip=ok)
ok = False
if not str(self.currentText()).strip():
self.clear(emit_search=False)
@ -223,6 +233,7 @@ class SearchBox2(QComboBox): # {{{
# Comes from the combobox itself
def keyPressEvent(self, event):
self.show_parse_error_action(False)
k = event.key()
if k in (Qt.Key.Key_Enter, Qt.Key.Key_Return):
return self.do_search()