From 8d19561f8527aeaac193cf2de3e9baf8cd01e35c Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 4 Nov 2009 18:06:34 +0000 Subject: [PATCH] fix up Terminal some more and make the searching work, and include a hacky backwards search --- terminatorlib/searchbar.py | 107 ++++++++++++++++++++++++++++++++++--- terminatorlib/terminal.py | 21 ++++++-- 2 files changed, 118 insertions(+), 10 deletions(-) diff --git a/terminatorlib/searchbar.py b/terminatorlib/searchbar.py index f4669046..9af5cdd4 100755 --- a/terminatorlib/searchbar.py +++ b/terminatorlib/searchbar.py @@ -7,26 +7,37 @@ import gtk import gobject from translation import _ +from config import Config +from util import dbg # pylint: disable-msg=R0904 class Searchbar(gtk.HBox): """Class implementing the Searchbar widget""" __gsignals__ = { - 'do-search': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), - 'next-search': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), 'end-search': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), } entry = None reslabel = None next = None + prev = None + + vte = None + config = None + + searchstring = None + searchrow = None + + searchits = None def __init__(self): """Class initialiser""" gtk.HBox.__init__(self) self.__gobject_init__() + self.config = Config() + # Search text self.entry = gtk.Entry() self.entry.set_activates_default(True) @@ -59,14 +70,25 @@ class Searchbar(gtk.HBox): self.next = gtk.Button(_('Next')) self.next.connect('clicked', self.next_search) + # Previous Button + self.prev = gtk.Button(_('Prev')) + self.prev.connect('clicked', self.prev_search) + self.pack_start(label, False) self.pack_start(self.entry) self.pack_start(self.reslabel, False) + self.pack_start(self.prev, False, False) self.pack_start(self.next, False, False) self.pack_end(close, False, False) self.hide() + def get_vte(self): + """Find our parent widget""" + parent = self.get_parent() + if parent: + self.vte = parent.vte + # pylint: disable-msg=W0613 def search_keypress(self, widget, event): """Handle keypress events""" @@ -74,17 +96,88 @@ class Searchbar(gtk.HBox): if key == 'Escape': self.end_search() + def start_search(self): + """Show ourselves""" + if not self.vte: + self.get_vte() + + self.show() + self.entry.grab_focus() + def do_search(self, widget): """Trap and re-emit the clicked signal""" - self.emit('do-search', widget) + self.searchhits = [] + searchtext = self.entry.get_text() + if searchtext == '': + return + + if searchtext != self.searchstring: + self.searchrow = self.get_vte_buffer_range()[0] + self.searchstring = searchtext + + self.reslabel.set_text(_("Searching scrollback")) + self.next_search(None) def next_search(self, widget): - """Trap and re-emit the next-search signal""" - self.emit('next-search', widget) + """Search forwards and jump to the next result, if any""" + # FIXME: I think we should emit a signal and have Terminal() do this. + startrow,endrow = self.get_vte_buffer_range() + while True: + if self.searchrow == endrow: + self.searchrow = startrow + self.reslabel.set_text(_("Finished search")) + self.next.hide() + self.prev.hide() + return + buffer = self.vte.get_text_range(self.searchrow, 0, + self.searchrow, -1, + self.search_character) - def end_search(self, widget): + index = buffer.find(self.searchstring) + if index != -1: + self.searchhits.append(self.searchrow) + self.search_hit(self.searchrow) + self.searchrow += 1 + return + self.searchrow += 1 + + def prev_search(self, widget): + """Jump back to the previous search""" + row = self.searchhits.pop() + position = self.get_parent().scrollbar_position() + while row >= position: + row = self.searchhits.pop() + self.search_hit(row) + self.searchrow -= 1 + + def search_hit(self, row): + """Update the UI for a search hit""" + dbg('Searchbar::search_hit row %d, history of %d' % (row, + len(self.searchhits))) + self.reslabel.set_text("%s %d" % (_('Found at row'), row)) + self.get_parent().scrollbar_jump(row) + self.next.show() + if len(self.searchhits) > 1: + self.prev.show() + else: + self.prev.hide() + + def search_character(self, widget, col, row, junk): + """We have to have a callback for each character""" + return(True) + + def get_vte_buffer_range(self): + """Get the range of a vte widget""" + column, endrow = self.vte.get_cursor_position() + startrow = max(0, endrow - self.config['scrollback_lines']) + return(startrow, endrow) + + def end_search(self, widget=None): """Trap and re-emit the end-search signal""" - self.emit('end-search', widget) + self.searchrow = 0 + self.searchstring = None + self.reslabel.set_text('') + self.emit('end-search') def get_search_term(self): """Return the currently set search term""" diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index 1f6f1249..fc54f4d5 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -110,6 +110,7 @@ class Terminal(gtk.VBox): self.titlebar.connect('create-group', self.really_create_group) self.searchbar = Searchbar() + self.searchbar.connect('end-search', self.on_search_done) self.show() self.pack_start(self.titlebar, False) @@ -645,6 +646,20 @@ class Terminal(gtk.VBox): def on_vte_focus_in(self, widget, event): self.emit('focus-in') + def scrollbar_jump(self, position): + """Move the scrollbar to a particular row""" + self.scrollbar.set_value(position) + + def scrollbar_position(self): + """Return the current position of the scrollbar""" + return(self.scrollbar.get_value()) + + def on_search_done(self, widget): + """We've finished searching, so clean up""" + self.searchbar.hide() + self.scrollbar.set_value(self.vte.get_cursor_position()[1]) + self.vte.grab_focus() + def on_edit_done(self, widget): """A child widget is done editing a label, return focus to VTE""" self.vte.grab_focus() @@ -761,7 +776,7 @@ class Terminal(gtk.VBox): def paste_clipboard(self, primary=False): """Paste one of the two clipboards""" - for term in self.terminator.get_target_terms(): + for term in self.terminator.get_target_terms(self): if primary: term.vte.paste_primary() else: @@ -810,7 +825,7 @@ class Terminal(gtk.VBox): self.vte.copy_clipboard() def key_paste(self): - self.vte-paste_clipboard() + self.vte.paste_clipboard() def key_toggle_scrollbar(self): self.do_scrollbar_toggle() @@ -819,7 +834,7 @@ class Terminal(gtk.VBox): self.zoom_orig () def key_search(self): - self.start_search() + self.searchbar.start_search() # bindings that should be moved to Terminator as they all just call # a function of Terminator. It would be cleaner if TerminatorTerm