/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: 2006-05-19 16:56:46 UTC
  • mfrom: (0.1.25 gannotate)
  • Revision ID: jelmer@samba.org-20060519165646-0d867938fdbc9097
Merge in Dan Loda's gannotate plugin and put it in annotate/

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
import gobject
22
22
import gtk
23
23
import pango
24
 
import re
25
24
 
26
 
from bzrlib import patiencediff, tsort
27
25
from bzrlib.errors import NoSuchRevision
28
 
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
29
26
 
30
27
from colormap import AnnotateColorMap, AnnotateColorSaturation
31
 
from bzrlib.plugins.gtk.revisionview import RevisionView
32
 
from bzrlib.plugins.gtk.window import Window
 
28
from logview import LogView
 
29
from spanselector import SpanSelector
33
30
 
34
31
 
35
32
(
42
39
) = range(6)
43
40
 
44
41
 
45
 
class GAnnotateWindow(Window):
 
42
class GAnnotateWindow(gtk.Window):
46
43
    """Annotate window."""
47
44
 
48
 
    def __init__(self, all=False, plain=False, parent=None, branch=None):
 
45
    def __init__(self, all=False, plain=False):
49
46
        self.all = all
50
47
        self.plain = plain
51
 
        self._branch = branch
52
48
        
53
 
        Window.__init__(self, parent)
 
49
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
54
50
        
55
51
        self.set_icon(self.render_icon(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON))
56
52
        self.annotate_colormap = AnnotateColorSaturation()
57
53
 
58
54
        self._create()
 
55
 
 
56
        if self.plain:
 
57
            self.span_selector.hide()
 
58
 
 
59
    def annotate(self, branch, file_id):
59
60
        self.revisions = {}
60
 
        self.history = []
61
 
        self._no_back = set()
62
 
 
63
 
    def annotate(self, tree, branch, file_id):
64
 
        self.annotations = []
65
 
        self.branch = branch
66
 
        self.tree = tree
67
 
        self.file_id = file_id
68
 
        self.revisionview.set_file_id(file_id)
69
 
        self.revision_id = getattr(tree, 'get_revision_id', 
70
 
                                   lambda: CURRENT_REVISION)()
71
61
        
72
 
        # [revision id, line number, author, revno, highlight color, line]
 
62
        # [revision id, line number, committer, revno, highlight color, line]
73
63
        self.annomodel = gtk.ListStore(gobject.TYPE_STRING,
74
64
                                       gobject.TYPE_STRING,
75
65
                                       gobject.TYPE_STRING,
81
71
        try:
82
72
            branch.lock_read()
83
73
            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)
88
74
            for line_no, (revision, revno, line)\
89
 
                    in enumerate(self._annotate(tree, file_id)):
 
75
                    in enumerate(self._annotate(branch, file_id)):
90
76
                if revision.revision_id == last_seen and not self.all:
91
 
                    revno = author = ""
 
77
                    revno = committer = ""
92
78
                else:
93
79
                    last_seen = revision.revision_id
94
 
                    author = ", ".join(revision.get_apparent_authors())
 
80
                    committer = revision.committer
95
81
 
96
82
                if revision.revision_id not in self.revisions:
97
83
                    self.revisions[revision.revision_id] = revision
98
84
 
99
85
                self.annomodel.append([revision.revision_id,
100
86
                                       line_no + 1,
101
 
                                       author,
 
87
                                       committer,
102
88
                                       revno,
103
89
                                       None,
104
90
                                       line.rstrip("\r\n")
105
91
                                      ])
106
 
                self.annotations.append(revision)
107
92
 
108
93
            if not self.plain:
109
 
                now = time.time()
110
 
                self.annomodel.foreach(self._highlight_annotation, now)
 
94
                self._set_oldest_newest()
 
95
                # Recall that calling activate_default will emit "span-changed",
 
96
                # so self._span_changed_cb will take care of initial highlighting
 
97
                self.span_selector.activate_default()
111
98
        finally:
112
99
            branch.repository.unlock()
113
100
            branch.unlock()
114
101
 
115
102
        self.annoview.set_model(self.annomodel)
116
103
        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)
120
104
 
121
105
    def jump_to_line(self, lineno):
122
106
        if lineno > len(self.annomodel) or lineno < 1:
125
109
            # bar?
126
110
            print("gannotate: Line number %d does't exist. Defaulting to "
127
111
                  "line 1." % lineno)
128
 
            return
129
112
        else:
130
113
            row = lineno - 1
131
114
 
132
115
        self.annoview.set_cursor(row)
133
 
        self.annoview.scroll_to_cell(row, use_align=True)
134
 
 
135
 
 
136
 
    def _annotate(self, tree, file_id):
137
 
        current_revision = FakeRevision(CURRENT_REVISION)
138
 
        current_revision.committer = self.branch.get_config().username()
139
 
        current_revision.timestamp = time.time()
140
 
        current_revision.message = '[Not yet committed]'
141
 
        current_revision.parent_ids = tree.get_parent_ids()
142
 
        current_revision.properties['branch-nick'] = self.branch._get_nick(local=True)
143
 
        current_revno = '%d?' % (self.branch.revno() + 1)
144
 
        repository = self.branch.repository
145
 
        if self.revision_id == CURRENT_REVISION:
146
 
            revision_id = self.branch.last_revision()
147
 
        else:
148
 
            revision_id = self.revision_id
149
 
        revision_cache = RevisionCache(repository, self.revisions)
150
 
        for origin, text in tree.annotate_iter(file_id):
 
116
 
 
117
    def _annotate(self, branch, file_id):
 
118
        rev_hist = branch.revision_history()
 
119
        repository = branch.repository
 
120
        rev_tree = repository.revision_tree(branch.last_revision())
 
121
        rev_id = rev_tree.inventory[file_id].revision
 
122
        weave = repository.weave_store.get_weave(file_id,
 
123
                                                 branch.get_transaction())
 
124
        
 
125
        revision_cache = RevisionCache(repository)
 
126
        for origin, text in weave.annotate_iter(rev_id):
151
127
            rev_id = origin
152
 
            if rev_id == CURRENT_REVISION:
153
 
                revision = current_revision
154
 
                revno = current_revno
155
 
            else:
156
 
                try:
157
 
                    revision = revision_cache.get_revision(rev_id)
158
 
                    revno = self.dotted.get(rev_id, 'merge')
159
 
                    if len(revno) > 15:
160
 
                        revno = 'merge'
161
 
                except NoSuchRevision:
162
 
                    revision = FakeRevision(rev_id)
163
 
                    revno = "?"
 
128
            try:
 
129
                revision = revision_cache.get_revision(rev_id)
 
130
                if rev_id in rev_hist:
 
131
                    revno = branch.revision_id_to_revno(rev_id)
 
132
                else:
 
133
                    revno = "merge"
 
134
            except NoSuchRevision:
 
135
                revision = NoneRevision(rev_id)
 
136
                revno = "?"
164
137
 
165
138
            yield revision, revno, text
166
139
 
 
140
    def _set_oldest_newest(self):
 
141
        rev_dates = map(lambda i: self.revisions[i].timestamp, self.revisions)
 
142
        oldest = min(rev_dates)
 
143
        newest = max(rev_dates)
 
144
 
 
145
        span = self._span_from_seconds(time.time() - oldest)
 
146
        self.span_selector.set_to_oldest_span(span)
 
147
        
 
148
        span = self._span_from_seconds(newest - oldest)
 
149
        self.span_selector.set_newest_to_oldest_span(span)
 
150
 
 
151
    def _span_from_seconds(self, seconds):
 
152
        return (seconds / (24 * 60 * 60))
 
153
    
 
154
    def _span_changed_cb(self, w, span):
 
155
        self.annotate_colormap.set_span(span)
 
156
        now = time.time()
 
157
        self.annomodel.foreach(self._highlight_annotation, now)
 
158
 
167
159
    def _highlight_annotation(self, model, path, iter, now):
168
160
        revision_id, = model.get(iter, REVISION_ID_COL)
169
161
        revision = self.revisions[revision_id]
170
162
        model.set(iter, HIGHLIGHT_COLOR_COL,
171
163
                  self.annotate_colormap.get_color(revision, now))
172
164
 
173
 
    def _selected_revision(self):
 
165
    def _show_log(self, w):
174
166
        (path, col) = self.annoview.get_cursor()
175
 
        if path is None:
176
 
            return None
177
 
        return self.annomodel[path][REVISION_ID_COL]
178
 
 
179
 
    def _activate_selected_revision(self, w):
180
 
        rev_id = self._selected_revision()
181
 
        if not rev_id or rev_id == NULL_REVISION:
182
 
            return
183
 
        selected = self.revisions[rev_id]
184
 
        self.revisionview.set_revision(selected)
185
 
        if (len(selected.parent_ids) != 0 and selected.parent_ids[0] not in
186
 
            self._no_back):
187
 
            enable_back = True
188
 
        else:
189
 
            enable_back = False
190
 
        self.back_button.set_sensitive(enable_back)
 
167
        rev_id = self.annomodel[path][REVISION_ID_COL]
 
168
        self.logview.set_revision(self.revisions[rev_id])
191
169
 
192
170
    def _create(self):
193
 
        self.revisionview = self._create_log_view()
 
171
        self.logview = self._create_log_view()
194
172
        self.annoview = self._create_annotate_view()
 
173
        self.span_selector = self._create_span_selector()
195
174
 
196
 
        vbox = gtk.VBox(False)
 
175
        vbox = gtk.VBox(False, 12)
 
176
        vbox.set_border_width(12)
197
177
        vbox.show()
198
178
 
199
179
        sw = gtk.ScrolledWindow()
200
180
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
201
181
        sw.set_shadow_type(gtk.SHADOW_IN)
202
182
        sw.add(self.annoview)
203
 
        self.annoview.gwindow = self
204
183
        sw.show()
205
 
 
206
 
        swbox = gtk.VBox()
207
 
        swbox.pack_start(sw)
208
 
        swbox.show()
209
 
 
210
 
        hbox = gtk.HBox(False, 6)
211
 
        self.back_button = self._create_back_button()
212
 
        hbox.pack_start(self.back_button, expand=False, fill=True)
213
 
        self.forward_button = self._create_forward_button()
214
 
        hbox.pack_start(self.forward_button, expand=False, fill=True)
215
 
        hbox.show()
216
 
        vbox.pack_start(hbox, expand=False, fill=True)
217
184
        
218
185
        self.pane = pane = gtk.VPaned()
219
 
        pane.add1(swbox)
220
 
        pane.add2(self.revisionview)
 
186
        pane.add1(sw)
 
187
        pane.add2(self.logview)
221
188
        pane.show()
222
189
        vbox.pack_start(pane, expand=True, fill=True)
223
 
 
224
 
        self._search = SearchBox()
225
 
        swbox.pack_start(self._search, expand=False, fill=True)
226
 
        accels = gtk.AccelGroup()
227
 
        accels.connect_group(gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
228
 
                             gtk.ACCEL_LOCKED,
229
 
                             self._search_by_text)
230
 
        accels.connect_group(gtk.keysyms.g, gtk.gdk.CONTROL_MASK,
231
 
                             gtk.ACCEL_LOCKED,
232
 
                             self._search_by_line)
233
 
        self.add_accel_group(accels)
 
190
        
 
191
        hbox = gtk.HBox(True, 6)
 
192
        hbox.pack_start(self.span_selector, expand=False, fill=True)
 
193
        hbox.pack_start(self._create_button_box(), expand=False, fill=True)
 
194
        hbox.show()
 
195
        vbox.pack_start(hbox, expand=False, fill=True)
234
196
 
235
197
        self.add(vbox)
236
198
 
237
 
    def _search_by_text(self, accel_group, window, key, modifiers):
238
 
        self._search.show_for('text')
239
 
        self._search.set_target(self.annoview, TEXT_LINE_COL)
240
 
 
241
 
    def _search_by_line(self, accel_group, window, key, modifiers):
242
 
        self._search.show_for('line')
243
 
        self._search.set_target(self.annoview, LINE_NUM_COL)
244
 
 
245
 
    def line_diff(self, tv, path, tvc):
246
 
        row = path[0]
247
 
        revision = self.annotations[row]
248
 
        repository = self.branch.repository
249
 
        if revision.revision_id == CURRENT_REVISION:
250
 
            tree1 = self.tree
251
 
            tree2 = self.tree.basis_tree()
252
 
        else:
253
 
            tree1 = repository.revision_tree(revision.revision_id)
254
 
            if len(revision.parent_ids) > 0:
255
 
                tree2 = repository.revision_tree(revision.parent_ids[0])
256
 
            else:
257
 
                tree2 = repository.revision_tree(NULL_REVISION)
258
 
        from bzrlib.plugins.gtk.diff import DiffWindow
259
 
        window = DiffWindow()
260
 
        window.set_diff("Diff for line %d" % (row+1), tree1, tree2)
261
 
        window.set_file(tree1.id2path(self.file_id))
262
 
        window.show()
263
 
 
264
 
 
265
199
    def _create_annotate_view(self):
266
200
        tv = gtk.TreeView()
267
201
        tv.set_rules_hint(False)
268
 
        tv.connect("cursor-changed", self._activate_selected_revision)
 
202
        tv.connect("cursor-changed", self._show_log)
269
203
        tv.show()
270
 
        tv.connect("row-activated", self.line_diff)
271
204
 
272
205
        cell = gtk.CellRendererText()
273
206
        cell.set_property("xalign", 1.0)
314
247
        col.add_attribute(cell, "text", TEXT_LINE_COL)
315
248
        tv.append_column(col)
316
249
 
317
 
        # FIXME: Now that C-f is now used for search by text we
318
 
        # may as well disable the auto search.
319
250
        tv.set_search_column(LINE_NUM_COL)
320
 
 
 
251
        
321
252
        return tv
322
253
 
 
254
    def _create_span_selector(self):
 
255
        ss = SpanSelector()
 
256
        ss.connect("span-changed", self._span_changed_cb)
 
257
        ss.show()
 
258
 
 
259
        return ss
 
260
 
323
261
    def _create_log_view(self):
324
 
        lv = RevisionView(self._branch)
 
262
        lv = LogView()
325
263
        lv.show()
 
264
 
326
265
        return lv
327
266
 
328
 
    def _create_back_button(self):
329
 
        button = gtk.Button()
330
 
        button.set_use_stock(True)
331
 
        button.set_label("gtk-go-back")
332
 
        button.connect("clicked", lambda w: self.go_back())
333
 
        button.set_relief(gtk.RELIEF_NONE)
334
 
        button.show()
335
 
        return button
336
 
 
337
 
    def _create_forward_button(self):
338
 
        button = gtk.Button()
339
 
        button.set_use_stock(True)
340
 
        button.set_label("gtk-go-forward")
341
 
        button.connect("clicked", lambda w: self.go_forward())
342
 
        button.set_relief(gtk.RELIEF_NONE)
343
 
        button.show()
344
 
        button.set_sensitive(False)
345
 
        return button
346
 
 
347
 
    def go_back(self):
348
 
        last_tree = self.tree
349
 
        rev_id = self._selected_revision()
350
 
        parent_id = self.revisions[rev_id].parent_ids[0]
351
 
        target_tree = self.branch.repository.revision_tree(parent_id)
352
 
        if self._go(target_tree):
353
 
            self.history.append(last_tree)
354
 
            self.forward_button.set_sensitive(True)
355
 
        else:
356
 
            self._no_back.add(parent_id)
357
 
            self.back_button.set_sensitive(False)
358
 
 
359
 
    def go_forward(self):
360
 
        if len(self.history) == 0:
361
 
            return
362
 
        target_tree = self.history.pop()
363
 
        if len(self.history) == 0:
364
 
            self.forward_button.set_sensitive(False)
365
 
        self._go(target_tree)
366
 
 
367
 
    def _go(self, target_tree):
368
 
        rev_id = self._selected_revision()
369
 
        if self.file_id in target_tree:
370
 
            offset = self.get_scroll_offset(target_tree)
371
 
            (row,), col = self.annoview.get_cursor()
372
 
            self.annotate(target_tree, self.branch, self.file_id)
373
 
            new_row = row+offset
374
 
            if new_row < 0:
375
 
                new_row = 0
376
 
            self.annoview.set_cursor(new_row)
377
 
            return True
378
 
        else:
379
 
            return False
380
 
 
381
 
    def get_scroll_offset(self, tree):
382
 
        old = self.tree.get_file(self.file_id)
383
 
        new = tree.get_file(self.file_id)
384
 
        (row,), col = self.annoview.get_cursor()
385
 
        matcher = patiencediff.PatienceSequenceMatcher(None, old.readlines(),
386
 
                                                       new.readlines())
387
 
        for i, j, n in matcher.get_matching_blocks():
388
 
            if i + n >= row:
389
 
                return j - i
390
 
 
391
 
 
392
 
class FakeRevision(object):
 
267
    def _create_button_box(self):
 
268
        box = gtk.HButtonBox()
 
269
        box.set_layout(gtk.BUTTONBOX_END)
 
270
        box.show()
 
271
        
 
272
        button = gtk.Button()
 
273
        button.set_use_stock(True)
 
274
        button.set_label("gtk-close")
 
275
        button.connect("clicked", lambda w: self.destroy())
 
276
        button.show()
 
277
        
 
278
        box.pack_start(button, expand=False, fill=False)
 
279
 
 
280
        return box
 
281
 
 
282
 
 
283
class NoneRevision:
393
284
    """ A fake revision.
394
285
 
395
286
    For when a revision is referenced but not present.
396
287
    """
397
288
 
398
 
    def __init__(self, revision_id, committer='?', nick=None):
 
289
    def __init__(self, revision_id):
399
290
        self.revision_id = revision_id
400
291
        self.parent_ids = []
401
 
        self.committer = committer
 
292
        self.committer = "?"
402
293
        self.message = "?"
403
294
        self.timestamp = 0.0
404
295
        self.timezone = 0
405
 
        self.properties = {}
406
 
 
407
 
    def get_apparent_authors(self):
408
 
        return [self.committer]
409
296
 
410
297
 
411
298
class RevisionCache(object):
412
299
    """A caching revision source"""
413
 
 
414
 
    def __init__(self, real_source, seed_cache=None):
 
300
    def __init__(self, real_source):
415
301
        self.__real_source = real_source
416
 
        if seed_cache is None:
417
 
            self.__cache = {}
418
 
        else:
419
 
            self.__cache = dict(seed_cache)
 
302
        self.__cache = {}
420
303
 
421
304
    def get_revision(self, revision_id):
422
305
        if revision_id not in self.__cache:
423
306
            revision = self.__real_source.get_revision(revision_id)
424
307
            self.__cache[revision_id] = revision
425
308
        return self.__cache[revision_id]
426
 
 
427
 
class SearchBox(gtk.HBox):
428
 
    """A button box for searching in text or lines of annotations"""
429
 
    def __init__(self):
430
 
        gtk.HBox.__init__(self, False, 6)
431
 
 
432
 
        # Close button
433
 
        button = gtk.Button()
434
 
        image = gtk.Image()
435
 
        image.set_from_stock('gtk-stop', gtk.ICON_SIZE_BUTTON)
436
 
        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)
440
 
 
441
 
        # Search entry
442
 
        label = gtk.Label()
443
 
        self._label = label
444
 
        self.pack_start(label, expand=False, fill=False)
445
 
 
446
 
        entry = gtk.Entry()
447
 
        self._entry = entry
448
 
        entry.connect("activate", lambda w, d: self._do_search(d),
449
 
                      'forward')
450
 
        self.pack_start(entry, expand=False, fill=False)
451
 
 
452
 
        # Next/previous buttons
453
 
        button = gtk.Button('_Next')
454
 
        image = gtk.Image()
455
 
        image.set_from_stock('gtk-go-forward', gtk.ICON_SIZE_BUTTON)
456
 
        button.set_image(image)
457
 
        button.connect("clicked", lambda w, d: self._do_search(d),
458
 
                       'forward')
459
 
        self.pack_start(button, expand=False, fill=False)
460
 
 
461
 
        button = gtk.Button('_Previous')
462
 
        image = gtk.Image()
463
 
        image.set_from_stock('gtk-go-back', gtk.ICON_SIZE_BUTTON)
464
 
        button.set_image(image)
465
 
        button.connect("clicked", lambda w, d: self._do_search(d),
466
 
                       'backward')
467
 
        self.pack_start(button, expand=False, fill=False)
468
 
 
469
 
        # Search options
470
 
        check = gtk.CheckButton('Match case')
471
 
        self._match_case = check
472
 
        self.pack_start(check, expand=False, fill=False)
473
 
 
474
 
        check = gtk.CheckButton('Regexp')
475
 
        check.connect("toggled", lambda w: self._set_label())
476
 
        self._regexp = check
477
 
        self.pack_start(check, expand=False, fill=False)
478
 
 
479
 
        self._view = None
480
 
        self._column = None
481
 
        # Note that we stay hidden (we do not call self.show_all())
482
 
 
483
 
 
484
 
    def show_for(self, kind):
485
 
        self._kind = kind
486
 
        self.show_all()
487
 
        self._set_label()
488
 
        # Hide unrelated buttons
489
 
        if kind == 'line':
490
 
            self._match_case.hide()
491
 
            self._regexp.hide()
492
 
        # Be ready
493
 
        self._entry.grab_focus()
494
 
 
495
 
    def _set_label(self):
496
 
        if self._kind == 'line':
497
 
            self._label.set_text('Find Line: ')
498
 
        else:
499
 
            if self._regexp.get_active():
500
 
                self._label.set_text('Find Regexp: ')
501
 
            else:
502
 
                self._label.set_text('Find Text: ')
503
 
 
504
 
    def set_target(self, view,column):
505
 
        self._view = view
506
 
        self._column = column
507
 
 
508
 
    def _match(self, model, iterator, column):
509
 
        matching_case = self._match_case.get_active()
510
 
        string, = model.get(iterator, column)
511
 
        key = self._entry.get_text()
512
 
        if self._regexp.get_active():
513
 
            if matching_case:
514
 
                match = re.compile(key).search(string, 1)
515
 
            else:
516
 
                match = re.compile(key, re.I).search(string, 1)
517
 
        else:
518
 
            if not matching_case:
519
 
                string = string.lower()
520
 
                key = key.lower()
521
 
            match = string.find(key) != -1
522
 
 
523
 
        return match
524
 
 
525
 
    def _iterate_rows_forward(self, model, start):
526
 
        model_size = len(model)
527
 
        current = start + 1
528
 
        while model_size != 0:
529
 
            if current >= model_size: current =  0
530
 
            yield model.get_iter_from_string('%d' % current)
531
 
            if current == start: raise StopIteration
532
 
            current += 1
533
 
 
534
 
    def _iterate_rows_backward(self, model, start):
535
 
        model_size = len(model)
536
 
        current = start - 1
537
 
        while model_size != 0:
538
 
            if current < 0: current = model_size - 1
539
 
            yield model.get_iter_from_string('%d' % current)
540
 
            if current == start: raise StopIteration
541
 
            current -= 1
542
 
 
543
 
    def _do_search(self, direction):
544
 
        if direction == 'forward':
545
 
            iterate = self._iterate_rows_forward
546
 
        else:
547
 
            iterate = self._iterate_rows_backward
548
 
 
549
 
        model, sel = self._view.get_selection().get_selected()
550
 
        if sel is None:
551
 
            start = 0
552
 
        else:
553
 
            path = model.get_string_from_iter(sel)
554
 
            start = int(path)
555
 
 
556
 
        for row in iterate(model, start):
557
 
            if self._match(model, row, self._column):
558
 
                path = model.get_path(row)
559
 
                self._view.set_cursor(path)
560
 
                self._view.scroll_to_cell(path, use_align=True)
561
 
                break