/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

Commit messages never contain config options

Show diffs side-by-side

added added

removed removed

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