/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-03-09 17:47:28 UTC
  • Revision ID: jelmer@samba.org-20070309174728-gljlmt9b7fu0rrn9
Add simple test for tortoise_bzr

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
29
29
 
30
30
from colormap import AnnotateColorMap, AnnotateColorSaturation
31
 
from bzrlib.plugins.gtk.revisionview import RevisionView
32
 
from bzrlib.plugins.gtk.window import Window
 
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):
 
47
    def __init__(self, all=False, plain=False):
49
48
        self.all = all
50
49
        self.plain = plain
51
50
        
52
 
        Window.__init__(self, parent)
 
51
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
53
52
        
54
53
        self.set_icon(self.render_icon(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON))
55
54
        self.annotate_colormap = AnnotateColorSaturation()
56
55
 
57
56
        self._create()
58
57
        self.revisions = {}
59
 
        self.history = []
60
 
        self._no_back = set()
61
58
 
62
59
    def annotate(self, tree, branch, file_id):
63
60
        self.annotations = []
64
61
        self.branch = branch
65
62
        self.tree = tree
66
63
        self.file_id = file_id
67
 
        self.revisionview.set_file_id(file_id)
68
64
        self.revision_id = getattr(tree, 'get_revision_id', 
69
65
                                   lambda: CURRENT_REVISION)()
70
66
        
71
 
        # [revision id, line number, author, revno, highlight color, line]
 
67
        # [revision id, line number, committer, revno, highlight color, line]
72
68
        self.annomodel = gtk.ListStore(gobject.TYPE_STRING,
73
69
                                       gobject.TYPE_STRING,
74
70
                                       gobject.TYPE_STRING,
80
76
        try:
81
77
            branch.lock_read()
82
78
            branch.repository.lock_read()
83
 
            self.dotted = {}
84
 
            revno_map = self.branch.get_revision_id_to_revno_map()
85
 
            for revision_id, revno in revno_map.iteritems():
86
 
                self.dotted[revision_id] = '.'.join(str(num) for num in revno)
87
79
            for line_no, (revision, revno, line)\
88
80
                    in enumerate(self._annotate(tree, file_id)):
89
81
                if revision.revision_id == last_seen and not self.all:
90
 
                    revno = author = ""
 
82
                    revno = committer = ""
91
83
                else:
92
84
                    last_seen = revision.revision_id
93
 
                    author = revision.get_apparent_author()
 
85
                    committer = revision.committer
94
86
 
95
87
                if revision.revision_id not in self.revisions:
96
88
                    self.revisions[revision.revision_id] = revision
97
89
 
98
90
                self.annomodel.append([revision.revision_id,
99
91
                                       line_no + 1,
100
 
                                       author,
 
92
                                       committer,
101
93
                                       revno,
102
94
                                       None,
103
95
                                       line.rstrip("\r\n")
113
105
 
114
106
        self.annoview.set_model(self.annomodel)
115
107
        self.annoview.grab_focus()
116
 
        my_revno = self.dotted.get(self.revision_id, 'current')
117
 
        title = '%s (%s) - gannotate' % (self.tree.id2path(file_id), my_revno)
118
 
        self.set_title(title)
119
108
 
120
109
    def jump_to_line(self, lineno):
121
110
        if lineno > len(self.annomodel) or lineno < 1:
131
120
        self.annoview.set_cursor(row)
132
121
        self.annoview.scroll_to_cell(row, use_align=True)
133
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
134
135
 
135
136
    def _annotate(self, tree, file_id):
136
137
        current_revision = FakeRevision(CURRENT_REVISION)
145
146
            revision_id = self.branch.last_revision()
146
147
        else:
147
148
            revision_id = self.revision_id
 
149
        dotted = self._dotted_revnos(repository, revision_id)
148
150
        revision_cache = RevisionCache(repository, self.revisions)
149
151
        for origin, text in tree.annotate_iter(file_id):
150
152
            rev_id = origin
154
156
            else:
155
157
                try:
156
158
                    revision = revision_cache.get_revision(rev_id)
157
 
                    revno = self.dotted.get(rev_id, 'merge')
 
159
                    revno = dotted.get(rev_id, 'merge')
158
160
                    if len(revno) > 15:
159
161
                        revno = 'merge'
160
162
                except NoSuchRevision:
175
177
            return None
176
178
        return self.annomodel[path][REVISION_ID_COL]
177
179
 
178
 
    def _activate_selected_revision(self, w):
 
180
    def _show_log(self, w):
179
181
        rev_id = self._selected_revision()
180
182
        if rev_id is None:
181
183
            return
182
 
        selected = self.revisions[rev_id]
183
 
        self.revisionview.set_revision(selected)
184
 
        if (len(selected.parent_ids) != 0 and selected.parent_ids[0] not in
185
 
            self._no_back):
186
 
            enable_back = True
187
 
        else:
188
 
            enable_back = False
189
 
        self.back_button.set_sensitive(enable_back)
 
184
        self.logview.set_revision(self.revisions[rev_id])
190
185
 
191
186
    def _create(self):
192
 
        self.revisionview = self._create_log_view()
 
187
        self.logview = self._create_log_view()
193
188
        self.annoview = self._create_annotate_view()
194
189
 
195
 
        vbox = gtk.VBox(False)
 
190
        vbox = gtk.VBox(False, 12)
 
191
        vbox.set_border_width(12)
196
192
        vbox.show()
197
193
 
198
194
        sw = gtk.ScrolledWindow()
201
197
        sw.add(self.annoview)
202
198
        self.annoview.gwindow = self
203
199
        sw.show()
204
 
 
205
 
        swbox = gtk.VBox()
206
 
        swbox.pack_start(sw)
207
 
        swbox.show()
208
 
 
209
 
        hbox = gtk.HBox(False, 6)
210
 
        self.back_button = self._create_back_button()
211
 
        hbox.pack_start(self.back_button, expand=False, fill=True)
212
 
        self.forward_button = self._create_forward_button()
213
 
        hbox.pack_start(self.forward_button, expand=False, fill=True)
214
 
        hbox.show()
215
 
        vbox.pack_start(hbox, expand=False, fill=True)
216
200
        
217
201
        self.pane = pane = gtk.VPaned()
218
 
        pane.add1(swbox)
219
 
        pane.add2(self.revisionview)
 
202
        pane.add1(sw)
 
203
        pane.add2(self.logview)
220
204
        pane.show()
221
205
        vbox.pack_start(pane, expand=True, fill=True)
222
206
 
223
207
        self._search = SearchBox()
224
 
        swbox.pack_start(self._search, expand=False, fill=True)
 
208
        vbox.pack_start(self._search, expand=False, fill=True)
225
209
        accels = gtk.AccelGroup()
226
210
        accels.connect_group(gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
227
211
                             gtk.ACCEL_LOCKED,
231
215
                             self._search_by_line)
232
216
        self.add_accel_group(accels)
233
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
 
234
224
        self.add(vbox)
235
225
 
236
226
    def _search_by_text(self, accel_group, window, key, modifiers):
241
231
        self._search.show_for('line')
242
232
        self._search.set_target(self.annoview, LINE_NUM_COL)
243
233
 
244
 
    def line_diff(self, tv, path, tvc):
 
234
    def row_diff(self, tv, path, tvc):
245
235
        row = path[0]
246
236
        revision = self.annotations[row]
247
237
        repository = self.branch.repository
256
246
                tree2 = repository.revision_tree(NULL_REVISION)
257
247
        from bzrlib.plugins.gtk.diff import DiffWindow
258
248
        window = DiffWindow()
259
 
        window.set_diff("Diff for line %d" % (row+1), tree1, tree2)
 
249
        window.set_diff("Diff for row %d" % (row+1), tree1, tree2)
260
250
        window.set_file(tree1.id2path(self.file_id))
261
251
        window.show()
262
252
 
264
254
    def _create_annotate_view(self):
265
255
        tv = gtk.TreeView()
266
256
        tv.set_rules_hint(False)
267
 
        tv.connect("cursor-changed", self._activate_selected_revision)
 
257
        tv.connect("cursor-changed", self._show_log)
268
258
        tv.show()
269
 
        tv.connect("row-activated", self.line_diff)
 
259
        tv.connect("row-activated", self.row_diff)
270
260
 
271
261
        cell = gtk.CellRendererText()
272
262
        cell.set_property("xalign", 1.0)
320
310
        return tv
321
311
 
322
312
    def _create_log_view(self):
323
 
        lv = RevisionView()
 
313
        lv = LogView()
324
314
        lv.show()
325
315
        return lv
326
316
 
327
 
    def _create_back_button(self):
 
317
    def _create_button_box(self):
 
318
        box = gtk.HButtonBox()
 
319
        box.set_layout(gtk.BUTTONBOX_END)
 
320
        box.show()
 
321
 
 
322
        button = gtk.Button()
 
323
        button.set_use_stock(True)
 
324
        button.set_label("gtk-close")
 
325
        button.connect("clicked", lambda w: self.destroy())
 
326
        button.show()
 
327
 
 
328
        box.pack_start(button, expand=False, fill=False)
 
329
 
 
330
        return box
 
331
 
 
332
    def _create_prev_button(self):
 
333
        box = gtk.HButtonBox()
 
334
        box.set_layout(gtk.BUTTONBOX_START)
 
335
        box.show()
 
336
        
328
337
        button = gtk.Button()
329
338
        button.set_use_stock(True)
330
339
        button.set_label("gtk-go-back")
331
340
        button.connect("clicked", lambda w: self.go_back())
332
 
        button.set_relief(gtk.RELIEF_NONE)
333
 
        button.show()
334
 
        return button
335
 
 
336
 
    def _create_forward_button(self):
337
 
        button = gtk.Button()
338
 
        button.set_use_stock(True)
339
 
        button.set_label("gtk-go-forward")
340
 
        button.connect("clicked", lambda w: self.go_forward())
341
 
        button.set_relief(gtk.RELIEF_NONE)
342
 
        button.show()
343
 
        button.set_sensitive(False)
344
 
        return button
 
341
        button.show()
 
342
        box.pack_start(button, expand=False, fill=False)
 
343
        return box
345
344
 
346
345
    def go_back(self):
347
 
        last_tree = self.tree
348
346
        rev_id = self._selected_revision()
349
347
        parent_id = self.revisions[rev_id].parent_ids[0]
350
 
        target_tree = self.branch.repository.revision_tree(parent_id)
351
 
        if self._go(target_tree):
352
 
            self.history.append(last_tree)
353
 
            self.forward_button.set_sensitive(True)
354
 
        else:
355
 
            self._no_back.add(parent_id)
356
 
            self.back_button.set_sensitive(False)
357
 
 
358
 
    def go_forward(self):
359
 
        if len(self.history) == 0:
360
 
            return
361
 
        target_tree = self.history.pop()
362
 
        if len(self.history) == 0:
363
 
            self.forward_button.set_sensitive(False)
364
 
        self._go(target_tree)
365
 
 
366
 
    def _go(self, target_tree):
367
 
        rev_id = self._selected_revision()
368
 
        if self.file_id in target_tree:
369
 
            offset = self.get_scroll_offset(target_tree)
 
348
        tree = self.branch.repository.revision_tree(parent_id)
 
349
        if self.file_id in tree:
 
350
            offset = self.get_scroll_offset(tree)
370
351
            (row,), col = self.annoview.get_cursor()
371
 
            self.annotate(target_tree, self.branch, self.file_id)
372
 
            new_row = row+offset
373
 
            if new_row < 0:
374
 
                new_row = 0
375
 
            self.annoview.set_cursor(new_row)
376
 
            return True
377
 
        else:
378
 
            return False
 
352
            self.annotate(tree, self.branch, self.file_id)
 
353
            self.annoview.set_cursor(row+offset)
379
354
 
380
355
    def get_scroll_offset(self, tree):
381
356
        old = self.tree.get_file(self.file_id)
388
363
                return j - i
389
364
 
390
365
 
 
366
 
391
367
class FakeRevision:
392
368
    """ A fake revision.
393
369
 
403
379
        self.timezone = 0
404
380
        self.properties = {}
405
381
 
406
 
    def get_apparent_author(self):
407
 
        return self.committer
408
 
 
409
382
 
410
383
class RevisionCache(object):
411
384
    """A caching revision source"""