/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: 2012-07-09 15:23:26 UTC
  • mto: This revision was merged to the branch mainline in revision 794.
  • Revision ID: jelmer@samba.org-20120709152326-dzxb8zoz0btull7n
Remove bzr-notify.

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
282
        cell.set_property("cell-background-gdk",
280
 
                          tv.get_style().bg[gtk.STATE_NORMAL])
281
 
        col = gtk.TreeViewColumn()
 
283
                          tv.get_style().bg[Gtk.StateType.NORMAL])
 
284
        col = Gtk.TreeViewColumn()
282
285
        col.set_resizable(False)
283
 
        col.pack_start(cell, expand=True)
 
286
        col.pack_start(cell, True)
284
287
        col.add_attribute(cell, "text", LINE_NUM_COL)
285
288
        tv.append_column(col)
286
289
 
287
 
        cell = gtk.CellRendererText()
 
290
        cell = Gtk.CellRendererText()
288
291
        cell.set_property("ypad", 0)
289
 
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
292
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
290
293
        cell.set_property("cell-background-gdk",
291
 
                          self.get_style().bg[gtk.STATE_NORMAL])
292
 
        col = gtk.TreeViewColumn("Committer")
 
294
                          self.get_style().bg[Gtk.StateType.NORMAL])
 
295
        col = Gtk.TreeViewColumn("Committer")
293
296
        col.set_resizable(True)
294
 
        col.pack_start(cell, expand=True)
 
297
        col.pack_start(cell, True)
295
298
        col.add_attribute(cell, "text", COMMITTER_COL)
296
299
        tv.append_column(col)
297
300
 
298
 
        cell = gtk.CellRendererText()
 
301
        cell = Gtk.CellRendererText()
299
302
        cell.set_property("xalign", 1.0)
300
303
        cell.set_property("ypad", 0)
301
304
        cell.set_property("cell-background-gdk",
302
 
                          self.get_style().bg[gtk.STATE_NORMAL])
303
 
        col = gtk.TreeViewColumn("Revno")
 
305
                          self.get_style().bg[Gtk.StateType.NORMAL])
 
306
        col = Gtk.TreeViewColumn("Revno")
304
307
        col.set_resizable(False)
305
 
        col.pack_start(cell, expand=True)
 
308
        col.pack_start(cell, True)
306
309
        col.add_attribute(cell, "markup", REVNO_COL)
307
310
        tv.append_column(col)
308
311
 
309
 
        cell = gtk.CellRendererText()
 
312
        cell = Gtk.CellRendererText()
310
313
        cell.set_property("ypad", 0)
311
314
        cell.set_property("family", "Monospace")
312
 
        col = gtk.TreeViewColumn()
 
315
        col = Gtk.TreeViewColumn()
313
316
        col.set_resizable(False)
314
 
        col.pack_start(cell, expand=True)
 
317
        col.pack_start(cell, True)
315
318
#        col.add_attribute(cell, "foreground", HIGHLIGHT_COLOR_COL)
316
319
        col.add_attribute(cell, "background", HIGHLIGHT_COLOR_COL)
317
320
        col.add_attribute(cell, "text", TEXT_LINE_COL)
318
321
        tv.append_column(col)
319
322
 
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)
 
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)
323
329
 
324
330
        return tv
325
331
 
326
332
    def _create_log_view(self):
327
 
        lv = LogView()
 
333
        lv = RevisionView(self._branch)
328
334
        lv.show()
329
335
        return lv
330
336
 
331
337
    def _create_back_button(self):
332
 
        button = gtk.Button()
 
338
        button = Gtk.Button()
333
339
        button.set_use_stock(True)
334
340
        button.set_label("gtk-go-back")
335
341
        button.connect("clicked", lambda w: self.go_back())
336
 
        button.set_relief(gtk.RELIEF_NONE)
 
342
        button.set_relief(Gtk.ReliefStyle.NONE)
337
343
        button.show()
338
344
        return button
339
345
 
340
346
    def _create_forward_button(self):
341
 
        button = gtk.Button()
 
347
        button = Gtk.Button()
342
348
        button.set_use_stock(True)
343
349
        button.set_label("gtk-go-forward")
344
350
        button.connect("clicked", lambda w: self.go_forward())
345
 
        button.set_relief(gtk.RELIEF_NONE)
 
351
        button.set_relief(Gtk.ReliefStyle.NONE)
346
352
        button.show()
347
353
        button.set_sensitive(False)
348
354
        return button
349
355
 
 
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
 
350
377
    def go_back(self):
351
378
        last_tree = self.tree
352
379
        rev_id = self._selected_revision()
371
398
        rev_id = self._selected_revision()
372
399
        if self.file_id in target_tree:
373
400
            offset = self.get_scroll_offset(target_tree)
374
 
            (row,), col = self.annoview.get_cursor()
 
401
            path, col = self.annoview.get_cursor()
 
402
            (row,) = path.get_indices()
375
403
            self.annotate(target_tree, self.branch, self.file_id)
376
404
            new_row = row+offset
377
405
            if new_row < 0:
378
406
                new_row = 0
379
 
            self.annoview.set_cursor(new_row)
 
407
            new_path = Gtk.TreePath(path=new_row)
 
408
            self.annoview.set_cursor(new_path, None, False)
380
409
            return True
381
410
        else:
382
411
            return False
384
413
    def get_scroll_offset(self, tree):
385
414
        old = self.tree.get_file(self.file_id)
386
415
        new = tree.get_file(self.file_id)
387
 
        (row,), col = self.annoview.get_cursor()
 
416
        path, col = self.annoview.get_cursor()
 
417
        (row,) = path.get_indices()
388
418
        matcher = patiencediff.PatienceSequenceMatcher(None, old.readlines(),
389
419
                                                       new.readlines())
390
420
        for i, j, n in matcher.get_matching_blocks():
392
422
                return j - i
393
423
 
394
424
 
395
 
 
396
 
class FakeRevision:
 
425
class FakeRevision(object):
397
426
    """ A fake revision.
398
427
 
399
428
    For when a revision is referenced but not present.
408
437
        self.timezone = 0
409
438
        self.properties = {}
410
439
 
 
440
    def get_apparent_authors(self):
 
441
        return [self.committer]
 
442
 
411
443
 
412
444
class RevisionCache(object):
413
445
    """A caching revision source"""
 
446
 
414
447
    def __init__(self, real_source, seed_cache=None):
415
448
        self.__real_source = real_source
416
449
        if seed_cache is None:
424
457
            self.__cache[revision_id] = revision
425
458
        return self.__cache[revision_id]
426
459
 
427
 
class SearchBox(gtk.HBox):
 
460
 
 
461
class SearchBox(Gtk.HBox):
428
462
    """A button box for searching in text or lines of annotations"""
429
463
    def __init__(self):
430
 
        gtk.HBox.__init__(self, False, 6)
 
464
        super(SearchBox, self).__init__(homogeneous=False, spacing=6)
431
465
 
432
466
        # Close button
433
 
        button = gtk.Button()
434
 
        image = gtk.Image()
435
 
        image.set_from_stock('gtk-stop', gtk.ICON_SIZE_BUTTON)
 
467
        button = Gtk.Button()
 
468
        image = Gtk.Image()
 
469
        image.set_from_stock('gtk-stop', Gtk.IconSize.BUTTON)
436
470
        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)
 
471
        button.set_relief(Gtk.ReliefStyle.NONE)
 
472
        button.connect("clicked", lambda w: self.hide())
 
473
        self.pack_start(button, False, False, 0)
440
474
 
441
475
        # Search entry
442
 
        label = gtk.Label()
 
476
        label = Gtk.Label()
443
477
        self._label = label
444
 
        self.pack_start(label, expand=False, fill=False)
 
478
        self.pack_start(label, False, False, 0)
445
479
 
446
 
        entry = gtk.Entry()
 
480
        entry = Gtk.Entry()
447
481
        self._entry = entry
448
482
        entry.connect("activate", lambda w, d: self._do_search(d),
449
483
                      'forward')
450
 
        self.pack_start(entry, expand=False, fill=False)
 
484
        self.pack_start(entry, False, False, 0)
451
485
 
452
486
        # Next/previous buttons
453
 
        button = gtk.Button('_Next')
454
 
        image = gtk.Image()
455
 
        image.set_from_stock('gtk-go-forward', gtk.ICON_SIZE_BUTTON)
 
487
        button = Gtk.Button(_i18n('_Next'), use_underline=True)
 
488
        image = Gtk.Image()
 
489
        image.set_from_stock('gtk-go-forward', Gtk.IconSize.BUTTON)
456
490
        button.set_image(image)
457
491
        button.connect("clicked", lambda w, d: self._do_search(d),
458
492
                       'forward')
459
 
        self.pack_start(button, expand=False, fill=False)
 
493
        self.pack_start(button, False, False, 0)
460
494
 
461
 
        button = gtk.Button('_Previous')
462
 
        image = gtk.Image()
463
 
        image.set_from_stock('gtk-go-back', gtk.ICON_SIZE_BUTTON)
 
495
        button = Gtk.Button(_i18n('_Previous'), use_underline=True)
 
496
        image = Gtk.Image()
 
497
        image.set_from_stock('gtk-go-back', Gtk.IconSize.BUTTON)
464
498
        button.set_image(image)
465
499
        button.connect("clicked", lambda w, d: self._do_search(d),
466
500
                       'backward')
467
 
        self.pack_start(button, expand=False, fill=False)
 
501
        self.pack_start(button, False, False, 0)
468
502
 
469
503
        # Search options
470
 
        check = gtk.CheckButton('Match case')
 
504
        check = Gtk.CheckButton('Match case')
471
505
        self._match_case = check
472
 
        self.pack_start(check, expand=False, fill=False)
 
506
        self.pack_start(check, False, False, 0)
473
507
 
474
 
        check = gtk.CheckButton('Regexp')
 
508
        check = Gtk.CheckButton('Regexp')
475
509
        check.connect("toggled", lambda w: self._set_label())
476
510
        self._regexp = check
477
 
        self.pack_start(check, expand=False, fill=False)
 
511
        self.pack_start(check, False, False, 0)
478
512
 
479
513
        self._view = None
480
514
        self._column = None
507
541
 
508
542
    def _match(self, model, iterator, column):
509
543
        matching_case = self._match_case.get_active()
510
 
        string, = model.get(iterator, column)
 
544
        cell_value, = model.get(iterator, column)
511
545
        key = self._entry.get_text()
512
 
        if self._regexp.get_active():
 
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():
513
551
            if matching_case:
514
 
                match = re.compile(key).search(string, 1)
 
552
                match = re.compile(key).search(cell_value, 1)
515
553
            else:
516
 
                match = re.compile(key, re.I).search(string, 1)
 
554
                match = re.compile(key, re.I).search(cell_value, 1)
517
555
        else:
518
556
            if not matching_case:
519
 
                string = string.lower()
 
557
                cell_value = cell_value.lower()
520
558
                key = key.lower()
521
 
            match = string.find(key) != -1
 
559
            match = cell_value.find(key) != -1
522
560
 
523
561
        return match
524
562
 
556
594
        for row in iterate(model, start):
557
595
            if self._match(model, row, self._column):
558
596
                path = model.get_path(row)
559
 
                self._view.set_cursor(path)
 
597
                self._view.set_cursor(path, None, False)
560
598
                self._view.scroll_to_cell(path, use_align=True)
561
599
                break