/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: 2011-12-06 16:57:32 UTC
  • Revision ID: jelmer@samba.org-20111206165732-0i7lcllq42wy6tao
Cope with the fact that Option doesn't support the help argument in bzr 2.4

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