File: searchbar.py

package info (click to toggle)
terminator 0.93-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 1,672 kB
  • ctags: 980
  • sloc: python: 7,551; sh: 22; makefile: 12
file content (194 lines) | stat: -rwxr-xr-x 6,025 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#!/usr/bin/python
# Terminator by Chris Jones <cmsj@tenshu.net>
# GPL v2 only
"""searchbar.py - classes necessary to provide a terminal search bar"""

import gtk
import gobject

from translation import _
from config import Config

# pylint: disable-msg=R0904
class Searchbar(gtk.HBox):
    """Class implementing the Searchbar widget"""

    __gsignals__ = {
        '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)
        self.entry.show()
        self.entry.connect('activate', self.do_search)
        self.entry.connect('key-press-event', self.search_keypress)

        # Label
        label = gtk.Label(_('Search:'))
        label.show()

        # Result label
        self.reslabel = gtk.Label('')
        self.reslabel.show()

        # Close Button
        close = gtk.Button()
        close.set_relief(gtk.RELIEF_NONE)
        close.set_focus_on_click(False)
        icon = gtk.Image()
        icon.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU)
        close.add(icon)
        close.set_name('terminator-search-close-button')
        if hasattr(close, 'set_tooltip_text'):
            close.set_tooltip_text(_('Close Search bar'))
        close.connect('clicked', self.end_search)
        close.show_all()

        # Next Button
        self.next = gtk.Button(_('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.show()
        self.prev.set_sensitive(False)
        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()
        self.set_no_show_all(True)

    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"""
        key = gtk.gdk.keyval_name(event.keyval)
        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"""
        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.set_sensitive(True)
        self.prev.set_sensitive(True)
        self.next_search(None)

    def next_search(self, widget):
        """Search forwards and jump to the next result, if any"""
        startrow,endrow = self.get_vte_buffer_range()
        while True:
            if self.searchrow == endrow:
                self.searchrow = startrow
                self.reslabel.set_text(_('No more results'))
                return
            buffer = self.vte.get_text_range(self.searchrow, 0, 
                                             self.searchrow, -1,
                                             self.search_character)

            index = buffer.find(self.searchstring)
            if index != -1:
                self.search_hit(self.searchrow)
                self.searchrow += 1
                return
            self.searchrow += 1

    # FIXME: There is an issue in switching search direction, probably because
    # we increment/decrement self.searchrow after each search iteration
    def prev_search(self, widget):
        """Jump back to the previous search"""
        startrow,endrow = self.get_vte_buffer_range()
        while True:
            if self.searchrow == startrow:
                self.searchrow = endrow
                self.reslabel.set_text(_('No more results'))
                return
            buffer = self.vte.get_text_range(self.searchrow, 0,
                                             self.searchrow, -1,
                                             self.search_character)

            index = buffer.find(self.searchstring)
            if index != -1:
                self.search_hit(self.searchrow)
                self.searchrow -= 1
                return
            self.searchrow -= 1

    def search_hit(self, row):
        """Update the UI for a search hit"""
        self.reslabel.set_text("%s %d" % (_('Found at row'), row))
        self.get_parent().scrollbar_jump(row)
        self.next.show()
        self.prev.show()

    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.searchrow = 0
        self.searchstring = None
        self.reslabel.set_text('')
        self.emit('end-search')

    def get_search_term(self):
        """Return the currently set search term"""
        return(self.entry.get_text())

gobject.type_register(Searchbar)