/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to annotate/gannotate.py

  • Committer: Jelmer Vernooij
  • Date: 2007-03-20 21:09:30 UTC
  • Revision ID: jelmer@samba.org-20070320210930-0650s8sy7nue5vyo
Don't use scrolling inside revisions in missing window.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
import time
18
18
 
19
 
from gi.repository import GObject
20
 
from gi.repository import Gdk
21
 
from gi.repository import Gtk
22
 
from gi.repository import Pango
 
19
import pygtk
 
20
pygtk.require("2.0")
 
21
import gobject
 
22
import gtk
 
23
import pango
23
24
import re
24
25
 
25
 
from bzrlib import patiencediff
 
26
from bzrlib import patiencediff, tsort
26
27
from bzrlib.errors import NoSuchRevision
27
28
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
28
29
 
29
 
from bzrlib.plugins.gtk.annotate.colormap import AnnotateColorSaturation
30
 
from bzrlib.plugins.gtk.i18n import _i18n
31
 
from bzrlib.plugins.gtk.revisionview import RevisionView
32
 
from bzrlib.plugins.gtk.window import Window
 
30
from colormap import AnnotateColorMap, AnnotateColorSaturation
 
31
from bzrlib.plugins.gtk.logview import LogView
33
32
 
34
33
 
35
34
(
42
41
) = range(6)
43
42
 
44
43
 
45
 
class GAnnotateWindow(Window):
 
44
class GAnnotateWindow(gtk.Window):
46
45
    """Annotate window."""
47
46
 
48
 
    def __init__(self, all=False, plain=False, parent=None, branch=None):
 
47
    def __init__(self, all=False, plain=False):
49
48
        self.all = all
50
49
        self.plain = plain
51
 
        self._branch = branch
52
 
 
53
 
        super(GAnnotateWindow, self).__init__(parent=parent)
54
 
 
55
 
        self.set_icon(
56
 
            self.render_icon_pixbuf(Gtk.STOCK_FIND, Gtk.IconSize.BUTTON))
 
50
        
 
51
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
 
52
        
 
53
        self.set_icon(self.render_icon(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON))
57
54
        self.annotate_colormap = AnnotateColorSaturation()
58
55
 
59
56
        self._create()
60
57
        self.revisions = {}
61
58
        self.history = []
62
 
        self._no_back = set()
63
59
 
64
60
    def annotate(self, tree, branch, file_id):
65
61
        self.annotations = []
66
62
        self.branch = branch
67
63
        self.tree = tree
68
64
        self.file_id = file_id
69
 
        self.revisionview.set_file_id(file_id)
70
65
        self.revision_id = getattr(tree, 'get_revision_id', 
71
66
                                   lambda: CURRENT_REVISION)()
72
 
 
73
 
        # [revision id, line number, author, revno, highlight color, line]
74
 
        self.annomodel = Gtk.ListStore(GObject.TYPE_STRING,
75
 
                                       GObject.TYPE_INT,
76
 
                                       GObject.TYPE_STRING,
77
 
                                       GObject.TYPE_STRING,
78
 
                                       GObject.TYPE_STRING,
79
 
                                       GObject.TYPE_STRING)
80
 
 
 
67
        
 
68
        # [revision id, line number, committer, revno, highlight color, line]
 
69
        self.annomodel = gtk.ListStore(gobject.TYPE_STRING,
 
70
                                       gobject.TYPE_STRING,
 
71
                                       gobject.TYPE_STRING,
 
72
                                       gobject.TYPE_STRING,
 
73
                                       gobject.TYPE_STRING,
 
74
                                       gobject.TYPE_STRING)
 
75
        
81
76
        last_seen = None
82
77
        try:
83
78
            branch.lock_read()
84
79
            branch.repository.lock_read()
85
 
            self.dotted = {}
86
 
            revno_map = self.branch.get_revision_id_to_revno_map()
87
 
            for revision_id, revno in revno_map.iteritems():
88
 
                self.dotted[revision_id] = '.'.join(str(num) for num in revno)
89
80
            for line_no, (revision, revno, line)\
90
 
                in enumerate(self._annotate(tree, file_id)):
 
81
                    in enumerate(self._annotate(tree, file_id)):
91
82
                if revision.revision_id == last_seen and not self.all:
92
 
                    revno = author = ""
 
83
                    revno = committer = ""
93
84
                else:
94
85
                    last_seen = revision.revision_id
95
 
                    author = ", ".join(revision.get_apparent_authors())
 
86
                    committer = revision.committer
96
87
 
97
88
                if revision.revision_id not in self.revisions:
98
89
                    self.revisions[revision.revision_id] = revision
99
90
 
100
91
                self.annomodel.append([revision.revision_id,
101
92
                                       line_no + 1,
102
 
                                       author,
 
93
                                       committer,
103
94
                                       revno,
104
95
                                       None,
105
96
                                       line.rstrip("\r\n")
106
 
                                       ])
 
97
                                      ])
107
98
                self.annotations.append(revision)
108
99
 
109
100
            if not self.plain:
115
106
 
116
107
        self.annoview.set_model(self.annomodel)
117
108
        self.annoview.grab_focus()
118
 
        my_revno = self.dotted.get(self.revision_id, 'current')
119
 
        title = '%s (%s) - gannotate' % (self.tree.id2path(file_id), my_revno)
120
 
        self.set_title(title)
121
109
 
122
110
    def jump_to_line(self, lineno):
123
111
        if lineno > len(self.annomodel) or lineno < 1:
126
114
            # bar?
127
115
            print("gannotate: Line number %d does't exist. Defaulting to "
128
116
                  "line 1." % lineno)
129
 
            return
 
117
            return
130
118
        else:
131
119
            row = lineno - 1
132
120
 
133
 
        tree_path = Gtk.TreePath(path=row)
134
 
        self.annoview.set_cursor(tree_path, None, False)
135
 
        self.annoview.scroll_to_cell(tree_path, use_align=True)
 
121
        self.annoview.set_cursor(row)
 
122
        self.annoview.scroll_to_cell(row, use_align=True)
 
123
 
 
124
    def _dotted_revnos(self, repository, revision_id):
 
125
        """Return a dict of revision_id -> dotted revno
 
126
        
 
127
        :param repository: The repository to get the graph from
 
128
        :param revision_id: The last revision for which this info is needed
 
129
        """
 
130
        graph = repository.get_revision_graph(revision_id)
 
131
        dotted = {}
 
132
        for n, revision_id, d, revno, e in tsort.merge_sort(graph, 
 
133
            revision_id, generate_revno=True):
 
134
            dotted[revision_id] = '.'.join(str(num) for num in revno)
 
135
        return dotted
136
136
 
137
137
    def _annotate(self, tree, file_id):
138
138
        current_revision = FakeRevision(CURRENT_REVISION)
140
140
        current_revision.timestamp = time.time()
141
141
        current_revision.message = '[Not yet committed]'
142
142
        current_revision.parent_ids = tree.get_parent_ids()
143
 
        current_revision.properties['branch-nick'] = self.branch._get_nick(local=True)
 
143
        current_revision.properties['branch-nick'] = self.branch.nick
144
144
        current_revno = '%d?' % (self.branch.revno() + 1)
145
145
        repository = self.branch.repository
146
146
        if self.revision_id == CURRENT_REVISION:
147
147
            revision_id = self.branch.last_revision()
148
148
        else:
149
149
            revision_id = self.revision_id
 
150
        dotted = self._dotted_revnos(repository, revision_id)
150
151
        revision_cache = RevisionCache(repository, self.revisions)
151
152
        for origin, text in tree.annotate_iter(file_id):
152
153
            rev_id = origin
156
157
            else:
157
158
                try:
158
159
                    revision = revision_cache.get_revision(rev_id)
159
 
                    revno = self.dotted.get(rev_id, 'merge')
 
160
                    revno = dotted.get(rev_id, 'merge')
160
161
                    if len(revno) > 15:
161
162
                        revno = 'merge'
162
163
                except NoSuchRevision:
168
169
    def _highlight_annotation(self, model, path, iter, now):
169
170
        revision_id, = model.get(iter, REVISION_ID_COL)
170
171
        revision = self.revisions[revision_id]
171
 
        # XXX sinzui 2011-08-12: What does get_color return?
172
 
        color = self.annotate_colormap.get_color(revision, now)
173
 
        model.set_value(iter, HIGHLIGHT_COLOR_COL, color)
 
172
        model.set(iter, HIGHLIGHT_COLOR_COL,
 
173
                  self.annotate_colormap.get_color(revision, now))
174
174
 
175
175
    def _selected_revision(self):
176
176
        (path, col) = self.annoview.get_cursor()
180
180
 
181
181
    def _activate_selected_revision(self, w):
182
182
        rev_id = self._selected_revision()
183
 
        if not rev_id or rev_id == NULL_REVISION:
 
183
        if rev_id is None:
184
184
            return
185
185
        selected = self.revisions[rev_id]
186
 
        self.revisionview.set_revision(selected)
187
 
        if (len(selected.parent_ids) != 0 and selected.parent_ids[0] not in
188
 
            self._no_back):
189
 
            enable_back = True
190
 
        else:
191
 
            enable_back = False
192
 
        self.back_button.set_sensitive(enable_back)
 
186
        self.logview.set_revision(selected)
 
187
        self.back_button.set_sensitive(len(selected.parent_ids) != 0)
193
188
 
194
189
    def _create(self):
195
 
        self.revisionview = self._create_log_view()
 
190
        self.logview = self._create_log_view()
196
191
        self.annoview = self._create_annotate_view()
197
192
 
198
 
        vbox = Gtk.VBox(homogeneous=False, spacing=0)
 
193
        vbox = gtk.VBox(False)
199
194
        vbox.show()
200
195
 
201
 
        sw = Gtk.ScrolledWindow()
202
 
        sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
203
 
        sw.set_shadow_type(Gtk.ShadowType.IN)
 
196
        sw = gtk.ScrolledWindow()
 
197
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
198
        sw.set_shadow_type(gtk.SHADOW_IN)
204
199
        sw.add(self.annoview)
205
200
        self.annoview.gwindow = self
206
201
        sw.show()
207
202
 
208
 
        swbox = Gtk.VBox()
209
 
        swbox.pack_start(sw, True, True, 0)
 
203
        swbox = gtk.VBox()
 
204
        swbox.pack_start(sw)
210
205
        swbox.show()
211
206
 
212
 
        hbox = Gtk.HBox(homogeneous=False, spacing=6)
 
207
        hbox = gtk.HBox(False, 6)
213
208
        self.back_button = self._create_back_button()
214
 
        hbox.pack_start(self.back_button, False, True, 0)
 
209
        hbox.pack_start(self.back_button, expand=False, fill=True)
215
210
        self.forward_button = self._create_forward_button()
216
 
        hbox.pack_start(self.forward_button, False, True, 0)
217
 
        self.find_button = self._create_find_button()
218
 
        hbox.pack_start(self.find_button, False, True, 0)
219
 
        self.goto_button = self._create_goto_button()
220
 
        hbox.pack_start(self.goto_button, False, True, 0)
 
211
        hbox.pack_start(self.forward_button, expand=False, fill=True)
221
212
        hbox.show()
222
 
        vbox.pack_start(hbox, False, True, 0)
223
 
 
224
 
        self.pane = pane = Gtk.Paned.new(Gtk.Orientation.VERTICAL)
 
213
        vbox.pack_start(hbox, expand=False, fill=True)
 
214
        
 
215
        self.pane = pane = gtk.VPaned()
225
216
        pane.add1(swbox)
226
 
        pane.add2(self.revisionview)
 
217
        pane.add2(self.logview)
227
218
        pane.show()
228
 
        vbox.pack_start(pane, True, True, 0)
 
219
        vbox.pack_start(pane, expand=True, fill=True)
229
220
 
230
221
        self._search = SearchBox()
231
 
        swbox.pack_start(self._search, False, True, 0)
232
 
        accels = Gtk.AccelGroup()
233
 
        accels.connect(Gdk.KEY_f, Gdk.ModifierType.CONTROL_MASK,
234
 
                             Gtk.AccelFlags.LOCKED,
 
222
        swbox.pack_start(self._search, expand=False, fill=True)
 
223
        accels = gtk.AccelGroup()
 
224
        accels.connect_group(gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
 
225
                             gtk.ACCEL_LOCKED,
235
226
                             self._search_by_text)
236
 
        accels.connect(Gdk.KEY_g, Gdk.ModifierType.CONTROL_MASK,
237
 
                             Gtk.AccelFlags.LOCKED,
 
227
        accels.connect_group(gtk.keysyms.g, gtk.gdk.CONTROL_MASK,
 
228
                             gtk.ACCEL_LOCKED,
238
229
                             self._search_by_line)
239
230
        self.add_accel_group(accels)
240
231
 
241
232
        self.add(vbox)
242
233
 
243
 
    def _search_by_text(self, *ignored): # (accel_group, window, key, modifiers):
 
234
    def _search_by_text(self, accel_group, window, key, modifiers):
244
235
        self._search.show_for('text')
245
236
        self._search.set_target(self.annoview, TEXT_LINE_COL)
246
237
 
247
 
    def _search_by_line(self, *ignored): # accel_group, window, key, modifiers):
 
238
    def _search_by_line(self, accel_group, window, key, modifiers):
248
239
        self._search.show_for('line')
249
240
        self._search.set_target(self.annoview, LINE_NUM_COL)
250
241
 
251
 
    def line_diff(self, tv, path, tvc):
252
 
        row = path.get_indices()[0]
 
242
    def row_diff(self, tv, path, tvc):
 
243
        row = path[0]
253
244
        revision = self.annotations[row]
254
245
        repository = self.branch.repository
255
246
        if revision.revision_id == CURRENT_REVISION:
262
253
            else:
263
254
                tree2 = repository.revision_tree(NULL_REVISION)
264
255
        from bzrlib.plugins.gtk.diff import DiffWindow
265
 
        window = DiffWindow(self)
266
 
        window.set_diff("Diff for line %d" % (row+1), tree1, tree2)
 
256
        window = DiffWindow()
 
257
        window.set_diff("Diff for row %d" % (row+1), tree1, tree2)
267
258
        window.set_file(tree1.id2path(self.file_id))
268
259
        window.show()
269
260
 
270
261
 
271
262
    def _create_annotate_view(self):
272
 
        tv = Gtk.TreeView()
 
263
        tv = gtk.TreeView()
273
264
        tv.set_rules_hint(False)
274
265
        tv.connect("cursor-changed", self._activate_selected_revision)
275
266
        tv.show()
276
 
        tv.connect("row-activated", self.line_diff)
 
267
        tv.connect("row-activated", self.row_diff)
277
268
 
278
 
        cell = Gtk.CellRendererText()
 
269
        cell = gtk.CellRendererText()
279
270
        cell.set_property("xalign", 1.0)
280
271
        cell.set_property("ypad", 0)
281
272
        cell.set_property("family", "Monospace")
282
273
        cell.set_property("cell-background-gdk",
283
 
                          tv.get_style().bg[Gtk.StateType.NORMAL])
284
 
        col = Gtk.TreeViewColumn()
 
274
                          tv.get_style().bg[gtk.STATE_NORMAL])
 
275
        col = gtk.TreeViewColumn()
285
276
        col.set_resizable(False)
286
 
        col.pack_start(cell, True)
 
277
        col.pack_start(cell, expand=True)
287
278
        col.add_attribute(cell, "text", LINE_NUM_COL)
288
279
        tv.append_column(col)
289
280
 
290
 
        cell = Gtk.CellRendererText()
 
281
        cell = gtk.CellRendererText()
291
282
        cell.set_property("ypad", 0)
292
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
 
283
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
293
284
        cell.set_property("cell-background-gdk",
294
 
                          self.get_style().bg[Gtk.StateType.NORMAL])
295
 
        col = Gtk.TreeViewColumn("Committer")
 
285
                          self.get_style().bg[gtk.STATE_NORMAL])
 
286
        col = gtk.TreeViewColumn("Committer")
296
287
        col.set_resizable(True)
297
 
        col.pack_start(cell, True)
 
288
        col.pack_start(cell, expand=True)
298
289
        col.add_attribute(cell, "text", COMMITTER_COL)
299
290
        tv.append_column(col)
300
291
 
301
 
        cell = Gtk.CellRendererText()
 
292
        cell = gtk.CellRendererText()
302
293
        cell.set_property("xalign", 1.0)
303
294
        cell.set_property("ypad", 0)
304
295
        cell.set_property("cell-background-gdk",
305
 
                          self.get_style().bg[Gtk.StateType.NORMAL])
306
 
        col = Gtk.TreeViewColumn("Revno")
 
296
                          self.get_style().bg[gtk.STATE_NORMAL])
 
297
        col = gtk.TreeViewColumn("Revno")
307
298
        col.set_resizable(False)
308
 
        col.pack_start(cell, True)
 
299
        col.pack_start(cell, expand=True)
309
300
        col.add_attribute(cell, "markup", REVNO_COL)
310
301
        tv.append_column(col)
311
302
 
312
 
        cell = Gtk.CellRendererText()
 
303
        cell = gtk.CellRendererText()
313
304
        cell.set_property("ypad", 0)
314
305
        cell.set_property("family", "Monospace")
315
 
        col = Gtk.TreeViewColumn()
 
306
        col = gtk.TreeViewColumn()
316
307
        col.set_resizable(False)
317
 
        col.pack_start(cell, True)
 
308
        col.pack_start(cell, expand=True)
318
309
#        col.add_attribute(cell, "foreground", HIGHLIGHT_COLOR_COL)
319
310
        col.add_attribute(cell, "background", HIGHLIGHT_COLOR_COL)
320
311
        col.add_attribute(cell, "text", TEXT_LINE_COL)
321
312
        tv.append_column(col)
322
313
 
323
 
        # interactive substring search
324
 
        def search_equal_func(model, column, key, iter):
325
 
            return model.get_value(iter, TEXT_LINE_COL).lower().find(key.lower()) == -1
326
 
 
327
 
        tv.set_enable_search(True)
328
 
        tv.set_search_equal_func(search_equal_func, None)
 
314
        # FIXME: Now that C-f is now used for search by text we
 
315
        # may as well disable the auto search.
 
316
        tv.set_search_column(LINE_NUM_COL)
329
317
 
330
318
        return tv
331
319
 
332
320
    def _create_log_view(self):
333
 
        lv = RevisionView(self._branch)
 
321
        lv = LogView()
334
322
        lv.show()
335
323
        return lv
336
324
 
337
325
    def _create_back_button(self):
338
 
        button = Gtk.Button()
 
326
        button = gtk.Button()
339
327
        button.set_use_stock(True)
340
328
        button.set_label("gtk-go-back")
341
329
        button.connect("clicked", lambda w: self.go_back())
342
 
        button.set_relief(Gtk.ReliefStyle.NONE)
 
330
        button.set_relief(gtk.RELIEF_NONE)
343
331
        button.show()
344
332
        return button
345
333
 
346
334
    def _create_forward_button(self):
347
 
        button = Gtk.Button()
 
335
        button = gtk.Button()
348
336
        button.set_use_stock(True)
349
337
        button.set_label("gtk-go-forward")
350
338
        button.connect("clicked", lambda w: self.go_forward())
351
 
        button.set_relief(Gtk.ReliefStyle.NONE)
 
339
        button.set_relief(gtk.RELIEF_NONE)
352
340
        button.show()
353
341
        button.set_sensitive(False)
354
342
        return button
355
343
 
356
 
    def _create_find_button(self):
357
 
        button = Gtk.Button()
358
 
        button.set_use_stock(True)
359
 
        button.set_label("gtk-find")
360
 
        button.set_tooltip_text("Search for text (Ctrl+F)")
361
 
        button.connect("clicked", self._search_by_text)
362
 
        button.set_relief(Gtk.ReliefStyle.NONE)
363
 
        button.show()
364
 
        button.set_sensitive(True)
365
 
        return button
366
 
 
367
 
    def _create_goto_button(self):
368
 
        button = Gtk.Button()
369
 
        button.set_label("Goto Line")
370
 
        button.set_tooltip_text("Scroll to a line by entering its number (Ctrl+G)")
371
 
        button.connect("clicked", self._search_by_line)
372
 
        button.set_relief(Gtk.ReliefStyle.NONE)
373
 
        button.show()
374
 
        button.set_sensitive(True)
375
 
        return button
376
 
 
377
344
    def go_back(self):
378
 
        last_tree = self.tree
 
345
        self.history.append(self.tree)
 
346
        self.forward_button.set_sensitive(True)
379
347
        rev_id = self._selected_revision()
380
348
        parent_id = self.revisions[rev_id].parent_ids[0]
381
349
        target_tree = self.branch.repository.revision_tree(parent_id)
382
 
        if self._go(target_tree):
383
 
            self.history.append(last_tree)
384
 
            self.forward_button.set_sensitive(True)
385
 
        else:
386
 
            self._no_back.add(parent_id)
387
 
            self.back_button.set_sensitive(False)
 
350
        self._go(target_tree)
388
351
 
389
352
    def go_forward(self):
390
353
        if len(self.history) == 0:
398
361
        rev_id = self._selected_revision()
399
362
        if self.file_id in target_tree:
400
363
            offset = self.get_scroll_offset(target_tree)
401
 
            path, col = self.annoview.get_cursor()
402
 
            (row,) = path.get_indices()
 
364
            (row,), col = self.annoview.get_cursor()
403
365
            self.annotate(target_tree, self.branch, self.file_id)
404
366
            new_row = row+offset
405
367
            if new_row < 0:
406
368
                new_row = 0
407
 
            new_path = Gtk.TreePath(path=new_row)
408
 
            self.annoview.set_cursor(new_path, None, False)
409
 
            return True
410
 
        else:
411
 
            return False
 
369
            self.annoview.set_cursor(new_row)
412
370
 
413
371
    def get_scroll_offset(self, tree):
414
372
        old = self.tree.get_file(self.file_id)
415
373
        new = tree.get_file(self.file_id)
416
 
        path, col = self.annoview.get_cursor()
417
 
        (row,) = path.get_indices()
 
374
        (row,), col = self.annoview.get_cursor()
418
375
        matcher = patiencediff.PatienceSequenceMatcher(None, old.readlines(),
419
376
                                                       new.readlines())
420
377
        for i, j, n in matcher.get_matching_blocks():
422
379
                return j - i
423
380
 
424
381
 
425
 
class FakeRevision(object):
 
382
 
 
383
class FakeRevision:
426
384
    """ A fake revision.
427
385
 
428
386
    For when a revision is referenced but not present.
437
395
        self.timezone = 0
438
396
        self.properties = {}
439
397
 
440
 
    def get_apparent_authors(self):
441
 
        return [self.committer]
442
 
 
443
398
 
444
399
class RevisionCache(object):
445
400
    """A caching revision source"""
446
 
 
447
401
    def __init__(self, real_source, seed_cache=None):
448
402
        self.__real_source = real_source
449
403
        if seed_cache is None:
457
411
            self.__cache[revision_id] = revision
458
412
        return self.__cache[revision_id]
459
413
 
460
 
 
461
 
class SearchBox(Gtk.HBox):
 
414
class SearchBox(gtk.HBox):
462
415
    """A button box for searching in text or lines of annotations"""
463
416
    def __init__(self):
464
 
        super(SearchBox, self).__init__(homogeneous=False, spacing=6)
 
417
        gtk.HBox.__init__(self, False, 6)
465
418
 
466
419
        # Close button
467
 
        button = Gtk.Button()
468
 
        image = Gtk.Image()
469
 
        image.set_from_stock('gtk-stop', Gtk.IconSize.BUTTON)
 
420
        button = gtk.Button()
 
421
        image = gtk.Image()
 
422
        image.set_from_stock('gtk-stop', gtk.ICON_SIZE_BUTTON)
470
423
        button.set_image(image)
471
 
        button.set_relief(Gtk.ReliefStyle.NONE)
472
 
        button.connect("clicked", lambda w: self.hide())
473
 
        self.pack_start(button, False, False, 0)
 
424
        button.set_relief(gtk.RELIEF_NONE)
 
425
        button.connect("clicked", lambda w: self.hide_all())
 
426
        self.pack_start(button, expand=False, fill=False)
474
427
 
475
428
        # Search entry
476
 
        label = Gtk.Label()
 
429
        label = gtk.Label()
477
430
        self._label = label
478
 
        self.pack_start(label, False, False, 0)
 
431
        self.pack_start(label, expand=False, fill=False)
479
432
 
480
 
        entry = Gtk.Entry()
 
433
        entry = gtk.Entry()
481
434
        self._entry = entry
482
435
        entry.connect("activate", lambda w, d: self._do_search(d),
483
436
                      'forward')
484
 
        self.pack_start(entry, False, False, 0)
 
437
        self.pack_start(entry, expand=False, fill=False)
485
438
 
486
439
        # Next/previous buttons
487
 
        button = Gtk.Button(_i18n('_Next'), use_underline=True)
488
 
        image = Gtk.Image()
489
 
        image.set_from_stock('gtk-go-forward', Gtk.IconSize.BUTTON)
 
440
        button = gtk.Button('_Next')
 
441
        image = gtk.Image()
 
442
        image.set_from_stock('gtk-go-forward', gtk.ICON_SIZE_BUTTON)
490
443
        button.set_image(image)
491
444
        button.connect("clicked", lambda w, d: self._do_search(d),
492
445
                       'forward')
493
 
        self.pack_start(button, False, False, 0)
 
446
        self.pack_start(button, expand=False, fill=False)
494
447
 
495
 
        button = Gtk.Button(_i18n('_Previous'), use_underline=True)
496
 
        image = Gtk.Image()
497
 
        image.set_from_stock('gtk-go-back', Gtk.IconSize.BUTTON)
 
448
        button = gtk.Button('_Previous')
 
449
        image = gtk.Image()
 
450
        image.set_from_stock('gtk-go-back', gtk.ICON_SIZE_BUTTON)
498
451
        button.set_image(image)
499
452
        button.connect("clicked", lambda w, d: self._do_search(d),
500
453
                       'backward')
501
 
        self.pack_start(button, False, False, 0)
 
454
        self.pack_start(button, expand=False, fill=False)
502
455
 
503
456
        # Search options
504
 
        check = Gtk.CheckButton('Match case')
 
457
        check = gtk.CheckButton('Match case')
505
458
        self._match_case = check
506
 
        self.pack_start(check, False, False, 0)
 
459
        self.pack_start(check, expand=False, fill=False)
507
460
 
508
 
        check = Gtk.CheckButton('Regexp')
 
461
        check = gtk.CheckButton('Regexp')
509
462
        check.connect("toggled", lambda w: self._set_label())
510
463
        self._regexp = check
511
 
        self.pack_start(check, False, False, 0)
 
464
        self.pack_start(check, expand=False, fill=False)
512
465
 
513
466
        self._view = None
514
467
        self._column = None
541
494
 
542
495
    def _match(self, model, iterator, column):
543
496
        matching_case = self._match_case.get_active()
544
 
        cell_value, = model.get(iterator, column)
 
497
        string, = model.get(iterator, column)
545
498
        key = self._entry.get_text()
546
 
        if column == LINE_NUM_COL:
547
 
            # FIXME: For goto-line there are faster algorithms than searching 
548
 
            # every line til we find the right one! -- mbp 2011-01-27
549
 
            return key.strip() == str(cell_value)
550
 
        elif self._regexp.get_active():
 
499
        if self._regexp.get_active():
551
500
            if matching_case:
552
 
                match = re.compile(key).search(cell_value, 1)
 
501
                match = re.compile(key).search(string, 1)
553
502
            else:
554
 
                match = re.compile(key, re.I).search(cell_value, 1)
 
503
                match = re.compile(key, re.I).search(string, 1)
555
504
        else:
556
505
            if not matching_case:
557
 
                cell_value = cell_value.lower()
 
506
                string = string.lower()
558
507
                key = key.lower()
559
 
            match = cell_value.find(key) != -1
 
508
            match = string.find(key) != -1
560
509
 
561
510
        return match
562
511
 
594
543
        for row in iterate(model, start):
595
544
            if self._match(model, row, self._column):
596
545
                path = model.get_path(row)
597
 
                self._view.set_cursor(path, None, False)
 
546
                self._view.set_cursor(path)
598
547
                self._view.scroll_to_cell(path, use_align=True)
599
548
                break