/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 18:31:29 UTC
  • mto: This revision was merged to the branch mainline in revision 518.
  • Revision ID: jelmer@samba.org-20080629183129-syqvz3xm5gqagzsx
Fix use of smart_add.

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)
139
139
        current_revision.timestamp = time.time()
140
140
        current_revision.message = '[Not yet committed]'
141
141
        current_revision.parent_ids = tree.get_parent_ids()
 
142
        current_revision.properties['branch-nick'] = self.branch.nick
142
143
        current_revno = '%d?' % (self.branch.revno() + 1)
143
144
        repository = self.branch.repository
144
145
        if self.revision_id == CURRENT_REVISION:
145
146
            revision_id = self.branch.last_revision()
146
147
        else:
147
148
            revision_id = self.revision_id
148
 
        dotted = self._dotted_revnos(repository, revision_id)
149
149
        revision_cache = RevisionCache(repository, self.revisions)
150
150
        for origin, text in tree.annotate_iter(file_id):
151
151
            rev_id = origin
155
155
            else:
156
156
                try:
157
157
                    revision = revision_cache.get_revision(rev_id)
158
 
                    revno = dotted.get(rev_id, 'merge')
 
158
                    revno = self.dotted.get(rev_id, 'merge')
159
159
                    if len(revno) > 15:
160
160
                        revno = 'merge'
161
161
                except NoSuchRevision:
176
176
            return None
177
177
        return self.annomodel[path][REVISION_ID_COL]
178
178
 
179
 
    def _show_log(self, w):
 
179
    def _activate_selected_revision(self, w):
180
180
        rev_id = self._selected_revision()
181
 
        if rev_id is None:
 
181
        if not rev_id or rev_id == NULL_REVISION:
182
182
            return
183
 
        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)
184
191
 
185
192
    def _create(self):
186
 
        self.logview = self._create_log_view()
 
193
        self.revisionview = self._create_log_view()
187
194
        self.annoview = self._create_annotate_view()
188
195
 
189
 
        vbox = gtk.VBox(False, 12)
190
 
        vbox.set_border_width(12)
 
196
        vbox = gtk.VBox(False)
191
197
        vbox.show()
192
198
 
193
199
        sw = gtk.ScrolledWindow()
196
202
        sw.add(self.annoview)
197
203
        self.annoview.gwindow = self
198
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)
199
217
        
200
218
        self.pane = pane = gtk.VPaned()
201
 
        pane.add1(sw)
202
 
        pane.add2(self.logview)
 
219
        pane.add1(swbox)
 
220
        pane.add2(self.revisionview)
203
221
        pane.show()
204
222
        vbox.pack_start(pane, expand=True, fill=True)
205
223
 
206
224
        self._search = SearchBox()
207
 
        vbox.pack_start(self._search, expand=False, fill=True)
 
225
        swbox.pack_start(self._search, expand=False, fill=True)
208
226
        accels = gtk.AccelGroup()
209
227
        accels.connect_group(gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
210
228
                             gtk.ACCEL_LOCKED,
214
232
                             self._search_by_line)
215
233
        self.add_accel_group(accels)
216
234
 
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
 
 
223
235
        self.add(vbox)
224
236
 
225
237
    def _search_by_text(self, accel_group, window, key, modifiers):
230
242
        self._search.show_for('line')
231
243
        self._search.set_target(self.annoview, LINE_NUM_COL)
232
244
 
233
 
    def row_diff(self, tv, path, tvc):
 
245
    def line_diff(self, tv, path, tvc):
234
246
        row = path[0]
235
247
        revision = self.annotations[row]
236
248
        repository = self.branch.repository
245
257
                tree2 = repository.revision_tree(NULL_REVISION)
246
258
        from bzrlib.plugins.gtk.diff import DiffWindow
247
259
        window = DiffWindow()
248
 
        window.set_diff("Diff for row %d" % (row+1), tree1, tree2)
 
260
        window.set_diff("Diff for line %d" % (row+1), tree1, tree2)
249
261
        window.set_file(tree1.id2path(self.file_id))
250
262
        window.show()
251
263
 
253
265
    def _create_annotate_view(self):
254
266
        tv = gtk.TreeView()
255
267
        tv.set_rules_hint(False)
256
 
        tv.connect("cursor-changed", self._show_log)
 
268
        tv.connect("cursor-changed", self._activate_selected_revision)
257
269
        tv.show()
258
 
        tv.connect("row-activated", self.row_diff)
 
270
        tv.connect("row-activated", self.line_diff)
259
271
 
260
272
        cell = gtk.CellRendererText()
261
273
        cell.set_property("xalign", 1.0)
309
321
        return tv
310
322
 
311
323
    def _create_log_view(self):
312
 
        lv = LogView()
 
324
        lv = RevisionView(self._branch)
313
325
        lv.show()
314
326
        return lv
315
327
 
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
 
        
 
328
    def _create_back_button(self):
336
329
        button = gtk.Button()
337
330
        button.set_use_stock(True)
338
331
        button.set_label("gtk-go-back")
339
332
        button.connect("clicked", lambda w: self.go_back())
340
 
        button.show()
341
 
        box.pack_start(button, expand=False, fill=False)
342
 
        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
343
346
 
344
347
    def go_back(self):
 
348
        last_tree = self.tree
345
349
        rev_id = self._selected_revision()
346
350
        parent_id = self.revisions[rev_id].parent_ids[0]
347
 
        tree = self.branch.repository.revision_tree(parent_id)
348
 
        if self.file_id in tree:
349
 
            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)
350
371
            (row,), col = self.annoview.get_cursor()
351
 
            self.annotate(tree, self.branch, self.file_id)
352
 
            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
353
380
 
354
381
    def get_scroll_offset(self, tree):
355
382
        old = self.tree.get_file(self.file_id)
362
389
                return j - i
363
390
 
364
391
 
365
 
 
366
392
class FakeRevision:
367
393
    """ A fake revision.
368
394
 
369
395
    For when a revision is referenced but not present.
370
396
    """
371
397
 
372
 
    def __init__(self, revision_id, committer='?'):
 
398
    def __init__(self, revision_id, committer='?', nick=None):
373
399
        self.revision_id = revision_id
374
400
        self.parent_ids = []
375
401
        self.committer = committer
376
402
        self.message = "?"
377
403
        self.timestamp = 0.0
378
404
        self.timezone = 0
379
 
        self.properties = []
 
405
        self.properties = {}
 
406
 
 
407
    def get_apparent_author(self):
 
408
        return self.committer
380
409
 
381
410
 
382
411
class RevisionCache(object):