From 82af0ad81f6ddb7b6c820d1d3eef06f4044d2cc2 Mon Sep 17 00:00:00 2001 From: Douglas Bacon Date: Mon, 22 Jun 2020 15:24:41 -0400 Subject: [PATCH 1/4] add option to not match case during search --- terminatorlib/regex.py | 4 +++ terminatorlib/searchbar.py | 67 ++++++++++++++++++++++++++------------ 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/terminatorlib/regex.py b/terminatorlib/regex.py index 029bec2a..c17f587d 100644 --- a/terminatorlib/regex.py +++ b/terminatorlib/regex.py @@ -11,6 +11,10 @@ from gi.repository import GLib, Vte # the corresponding bits are defined here: # https://vcs.pcre.org/pcre2/code/trunk/src/pcre2.h.in?view=markup PCRE2_MULTILINE = 0x00000400 +PCRE2_CASELESS = 0x00000008 + +GLIB_CASELESS = GLib.RegexCompileFlags.CASELESS + FLAGS_GLIB = (GLib.RegexCompileFlags.OPTIMIZE | GLib.RegexCompileFlags.MULTILINE) if hasattr(Vte, 'REGEX_FLAGS_DEFAULT'): FLAGS_PCRE2 = (Vte.REGEX_FLAGS_DEFAULT | PCRE2_MULTILINE) diff --git a/terminatorlib/searchbar.py b/terminatorlib/searchbar.py index 0ecb36b1..4160ddfd 100644 --- a/terminatorlib/searchbar.py +++ b/terminatorlib/searchbar.py @@ -37,6 +37,10 @@ class Searchbar(Gtk.HBox): """Class initialiser""" GObject.GObject.__init__(self) + # default regex flags are not CASELESS + self.regex_flags_pcre2 = regex.FLAGS_PCRE2 + self.regex_flags_glib = regex.FLAGS_GLIB + self.config = Config() self.get_style_context().add_class("terminator-terminal-searchbar") @@ -66,21 +70,29 @@ class Searchbar(Gtk.HBox): close.show_all() # Next Button - self.next = Gtk.Button(_('Next')) + self.next = Gtk.Button.new_with_label('Next') self.next.show() self.next.set_sensitive(False) self.next.connect('clicked', self.next_search) # Previous Button - self.prev = Gtk.Button(_('Prev')) + self.prev = Gtk.Button.new_with_label('Prev') self.prev.show() self.prev.set_sensitive(False) self.prev.connect('clicked', self.prev_search) + # Match Case checkbox + self.match_case = Gtk.CheckButton.new_with_label('Match Case') + self.match_case.show() + self.match_case.set_sensitive(True) + self.match_case.set_active(True) + self.match_case.connect('toggled', self.match_case_toggled) + # Wrap checkbox - self.wrap = Gtk.CheckButton(_('Wrap')) + self.wrap = Gtk.CheckButton.new_with_label('Wrap') self.wrap.show() self.wrap.set_sensitive(True) + self.wrap.set_active(True) self.wrap.connect('toggled', self.wrap_toggled) self.pack_start(label, False, True, 0) @@ -88,6 +100,7 @@ class Searchbar(Gtk.HBox): self.pack_start(self.prev, False, False, 0) self.pack_start(self.next, False, False, 0) self.pack_start(self.wrap, False, False, 0) + self.pack_start(self.match_case, False, False, 0) self.pack_end(close, False, False, 0) self.hide() @@ -100,11 +113,28 @@ class Searchbar(Gtk.HBox): self.prev.set_sensitive(True) self.next.set_sensitive(True) + def match_case_toggled(self, toggled): + """Handles Match Case checkbox toggles""" + + toggled_state = toggled.get_active() + if not toggled_state: + # Add the CASELESS regex flags when the checkbox is not checked. + self.regex_flags_pcre2 = (regex.FLAGS_PCRE2 | regex.PCRE2_CASELESS) + self.regex_flags_glib = (regex.FLAGS_GLIB | regex.GLIB_CASELESS) + else: + # Default state of the check box is unchecked. CASELESS regex flags are not added. + self.regex_flags_pcre2 = regex.FLAGS_PCRE2 + self.regex_flags_glib = regex.FLAGS_GLIB + + self.do_search(self.entry) # Start a new search everytime the check box is toggled. + def get_vte(self): """Find our parent widget""" parent = self.get_parent() if parent: self.vte = parent.vte + #turn on wrap by default + self.vte.search_set_wrap_around(True) # pylint: disable-msg=W0613 def search_keypress(self, widget, event): @@ -132,24 +162,21 @@ class Searchbar(Gtk.HBox): if searchtext == '': return - if searchtext != self.searchstring: - self.searchstring = searchtext - self.searchre = None - - if regex.FLAGS_PCRE2: - try: - self.searchre = Vte.Regex.new_for_search(searchtext, len(searchtext), regex.FLAGS_PCRE2) - dbg('search RE: %s' % self.searchre) - self.vte.search_set_regex(self.searchre, 0) - except GLib.Error: - # happens when PCRE2 support is not builtin (Ubuntu < 19.10) - pass - - if not self.searchre: - # fall back to old GLib regex - self.searchre = GLib.Regex(searchtext, regex.FLAGS_GLIB, 0) + self.searchre = None + if regex.FLAGS_PCRE2: + try: + self.searchre = Vte.Regex.new_for_search(searchtext, len(searchtext), self.regex_flags_pcre2) dbg('search RE: %s' % self.searchre) - self.vte.search_set_gregex(self.searchre, 0) + self.vte.search_set_regex(self.searchre, 0) + except GLib.Error: + # happens when PCRE2 support is not builtin (Ubuntu < 19.10) + pass + + if not self.searchre: + # fall back to old GLib regex + self.searchre = GLib.Regex(searchtext, self.regex_flags_glib, 0) + dbg('search RE: %s' % self.searchre) + self.vte.search_set_gregex(self.searchre, 0) self.next.set_sensitive(True) self.prev.set_sensitive(True) From e9a8c42fff98d8606e7150da435e5aec93cd0861 Mon Sep 17 00:00:00 2001 From: robertoetcheverryr Date: Tue, 23 Jun 2020 10:08:17 -0300 Subject: [PATCH 2/4] Fixed bug in prefseditor.py --- terminatorlib/prefseditor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminatorlib/prefseditor.py b/terminatorlib/prefseditor.py index 101c357a..28c358e0 100755 --- a/terminatorlib/prefseditor.py +++ b/terminatorlib/prefseditor.py @@ -863,7 +863,7 @@ class PrefsEditor: elif selected == 2: value = 'ascii-del' elif selected == 3: - value == 'escape-sequence' + value = 'escape-sequence' else: value = 'automatic' self.config['backspace_binding'] = value From ff53f737f80da6d9aaeb7b9c1a117ff07a1e6870 Mon Sep 17 00:00:00 2001 From: Douglas Bacon Date: Wed, 24 Jun 2020 10:16:58 -0400 Subject: [PATCH 3/4] catch TypeError when PCRE2 is not available --- terminatorlib/searchbar.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/terminatorlib/searchbar.py b/terminatorlib/searchbar.py index 4160ddfd..35c64dc1 100644 --- a/terminatorlib/searchbar.py +++ b/terminatorlib/searchbar.py @@ -119,7 +119,13 @@ class Searchbar(Gtk.HBox): toggled_state = toggled.get_active() if not toggled_state: # Add the CASELESS regex flags when the checkbox is not checked. - self.regex_flags_pcre2 = (regex.FLAGS_PCRE2 | regex.PCRE2_CASELESS) + try: + self.regex_flags_pcre2 = (regex.FLAGS_PCRE2 | regex.PCRE2_CASELESS) + except TypeError: + # if PCRE2 support is not available + pass + + # The code will fall back to use this GLib regex when PCRE2 is not available self.regex_flags_glib = (regex.FLAGS_GLIB | regex.GLIB_CASELESS) else: # Default state of the check box is unchecked. CASELESS regex flags are not added. From 279a3e10e6d4de4c6a81e7f7ac3bda67bebe57fb Mon Sep 17 00:00:00 2001 From: dkmvs <67212386+dkmvs@users.noreply.github.com> Date: Sat, 27 Jun 2020 23:42:04 +0100 Subject: [PATCH 4/4] Fix: Allow Key Bindings with Shift-Modified Keys This commit allows to set key bindings that contain a key modified by a Shift key (e.g. `Ctrl + {`). For example, after pressing `Ctrl + Shift + [`, a key binding will be set to `Ctrl + {` as opposed to `Ctrl + Shift + {` as before. This is achieved by checking whether a key changes its value when a Shift key is down. If it does, then the Shift modifier is removed from `mods`. One exception: if a key binding contains a letter then the Shift modifier is not removed. This is because, for some reason, a key value of a letter is never modified by the Shift modifier and always corresponds to a key value of a lowercase character. This is already handled in `terminatorlib/keybindings.py`. Resolves: #149 --- terminatorlib/prefseditor.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/terminatorlib/prefseditor.py b/terminatorlib/prefseditor.py index 28c358e0..50365bec 100755 --- a/terminatorlib/prefseditor.py +++ b/terminatorlib/prefseditor.py @@ -1550,6 +1550,21 @@ class PrefsEditor: def on_cellrenderer_accel_edited(self, liststore, path, key, mods, _code): """Handle an edited keybinding""" + if mods & Gdk.ModifierType.SHIFT_MASK: + key_with_shift = Gdk.Keymap.translate_keyboard_state( + self.keybindings.keymap, + hardware_keycode=_code, + state=Gdk.ModifierType.SHIFT_MASK, + group=0, + ) + keyval_lower, keyval_upper = Gdk.keyval_convert_case(key) + + # Remove the Shift modifier from `mods` if a new key binding doesn't + # contain a letter and its key value (`key`) can't be modified by a + # Shift key. + if key_with_shift.level != 0 and keyval_lower == keyval_upper: + mods = Gdk.ModifierType(mods & ~Gdk.ModifierType.SHIFT_MASK) + celliter = liststore.get_iter_from_string(path) liststore.set(celliter, 2, key, 3, mods)