/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: 2008-06-29 19:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 515.
  • Revision ID: jelmer@samba.org-20080629191834-ha2ecpv5szt96nge
Make sure signed testament matches repository data.

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