/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-03 14:19:44 UTC
  • Revision ID: jelmer@samba.org-20070203141944-oa6iqhetsy0slqo0
Fix references to dialog.

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