/b-gtk/fix-viz

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

« back to all changes in this revision

Viewing changes to annotate/gannotate.py

  • Committer: Jelmer Vernooij
  • Date: 2007-02-01 15:50:40 UTC
  • Revision ID: jelmer@samba.org-20070201155040-3hq4mfbxs99kzazy
add framework for tests.

Show diffs side-by-side

added added

removed removed

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