/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-01-30 17:26:34 UTC
  • Revision ID: jelmer@samba.org-20070130172634-ov7ucauynnp5vso6
Warn about incompatible versions (taken from bzrtools, thanks Aaron).

Update version to 0.14.

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 logview import LogView
 
32
from spanselector import SpanSelector
33
33
 
34
34
 
35
35
(
42
42
) = range(6)
43
43
 
44
44
 
45
 
class GAnnotateWindow(Window):
 
45
class GAnnotateWindow(gtk.Window):
46
46
    """Annotate window."""
47
47
 
48
 
    def __init__(self, all=False, plain=False, parent=None, branch=None):
 
48
    def __init__(self, all=False, plain=False):
49
49
        self.all = all
50
50
        self.plain = plain
51
 
        self._branch = branch
52
51
        
53
 
        Window.__init__(self, parent)
 
52
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
54
53
        
55
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
67
        
72
 
        # [revision id, line number, author, revno, highlight color, line]
 
68
        # [revision id, line number, committer, revno, highlight color, line]
73
69
        self.annomodel = gtk.ListStore(gobject.TYPE_STRING,
74
70
                                       gobject.TYPE_STRING,
75
71
                                       gobject.TYPE_STRING,
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
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 = revision.get_apparent_author()
 
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")
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:
132
121
        self.annoview.set_cursor(row)
133
122
        self.annoview.scroll_to_cell(row, use_align=True)
134
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.nick
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:
176
177
            return None
177
178
        return self.annomodel[path][REVISION_ID_COL]
178
179
 
179
 
    def _activate_selected_revision(self, w):
 
180
    def _show_log(self, w):
180
181
        rev_id = self._selected_revision()
181
 
        if not rev_id or rev_id == NULL_REVISION:
 
182
        if rev_id is None:
182
183
            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)
 
184
        self.logview.set_revision(self.revisions[rev_id])
191
185
 
192
186
    def _create(self):
193
 
        self.revisionview = self._create_log_view()
 
187
        self.logview = self._create_log_view()
194
188
        self.annoview = self._create_annotate_view()
195
189
 
196
 
        vbox = gtk.VBox(False)
 
190
        vbox = gtk.VBox(False, 12)
 
191
        vbox.set_border_width(12)
197
192
        vbox.show()
198
193
 
199
194
        sw = gtk.ScrolledWindow()
202
197
        sw.add(self.annoview)
203
198
        self.annoview.gwindow = self
204
199
        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
200
        
218
201
        self.pane = pane = gtk.VPaned()
219
 
        pane.add1(swbox)
220
 
        pane.add2(self.revisionview)
 
202
        pane.add1(sw)
 
203
        pane.add2(self.logview)
221
204
        pane.show()
222
205
        vbox.pack_start(pane, expand=True, fill=True)
223
206
 
224
207
        self._search = SearchBox()
225
 
        swbox.pack_start(self._search, expand=False, fill=True)
 
208
        vbox.pack_start(self._search, expand=False, fill=True)
226
209
        accels = gtk.AccelGroup()
227
210
        accels.connect_group(gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
228
211
                             gtk.ACCEL_LOCKED,
232
215
                             self._search_by_line)
233
216
        self.add_accel_group(accels)
234
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
 
235
224
        self.add(vbox)
236
225
 
237
226
    def _search_by_text(self, accel_group, window, key, modifiers):
242
231
        self._search.show_for('line')
243
232
        self._search.set_target(self.annoview, LINE_NUM_COL)
244
233
 
245
 
    def line_diff(self, tv, path, tvc):
 
234
    def row_diff(self, tv, path, tvc):
246
235
        row = path[0]
247
236
        revision = self.annotations[row]
248
237
        repository = self.branch.repository
255
244
                tree2 = repository.revision_tree(revision.parent_ids[0])
256
245
            else:
257
246
                tree2 = repository.revision_tree(NULL_REVISION)
258
 
        from bzrlib.plugins.gtk.diff import DiffWindow
 
247
        from bzrlib.plugins.gtk.viz.diffwin import DiffWindow
259
248
        window = DiffWindow()
260
 
        window.set_diff("Diff for line %d" % (row+1), tree1, tree2)
 
249
        window.set_diff("Diff for row %d" % (row+1), tree1, tree2)
261
250
        window.set_file(tree1.id2path(self.file_id))
262
251
        window.show()
263
252
 
265
254
    def _create_annotate_view(self):
266
255
        tv = gtk.TreeView()
267
256
        tv.set_rules_hint(False)
268
 
        tv.connect("cursor-changed", self._activate_selected_revision)
 
257
        tv.connect("cursor-changed", self._show_log)
269
258
        tv.show()
270
 
        tv.connect("row-activated", self.line_diff)
 
259
        tv.connect("row-activated", self.row_diff)
271
260
 
272
261
        cell = gtk.CellRendererText()
273
262
        cell.set_property("xalign", 1.0)
321
310
        return tv
322
311
 
323
312
    def _create_log_view(self):
324
 
        lv = RevisionView(self._branch)
 
313
        lv = LogView()
325
314
        lv.show()
 
315
 
326
316
        return lv
327
317
 
328
 
    def _create_back_button(self):
 
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
        
329
338
        button = gtk.Button()
330
339
        button.set_use_stock(True)
331
340
        button.set_label("gtk-go-back")
332
341
        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
 
342
        button.show()
 
343
        box.pack_start(button, expand=False, fill=False)
 
344
        return box
346
345
 
347
346
    def go_back(self):
348
 
        last_tree = self.tree
349
347
        rev_id = self._selected_revision()
350
348
        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)
 
349
        tree = self.branch.repository.revision_tree(parent_id)
 
350
        if self.file_id in tree:
 
351
            offset = self.get_scroll_offset(tree)
371
352
            (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
 
353
            self.annotate(tree, self.branch, self.file_id)
 
354
            self.annoview.set_cursor(row+offset)
380
355
 
381
356
    def get_scroll_offset(self, tree):
382
357
        old = self.tree.get_file(self.file_id)
389
364
                return j - i
390
365
 
391
366
 
 
367
 
392
368
class FakeRevision:
393
369
    """ A fake revision.
394
370
 
395
371
    For when a revision is referenced but not present.
396
372
    """
397
373
 
398
 
    def __init__(self, revision_id, committer='?', nick=None):
 
374
    def __init__(self, revision_id, committer='?'):
399
375
        self.revision_id = revision_id
400
376
        self.parent_ids = []
401
377
        self.committer = committer
402
378
        self.message = "?"
403
379
        self.timestamp = 0.0
404
380
        self.timezone = 0
405
 
        self.properties = {}
406
 
 
407
 
    def get_apparent_author(self):
408
 
        return self.committer
409
381
 
410
382
 
411
383
class RevisionCache(object):