/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-03-09 13:47:52 UTC
  • mto: This revision was merged to the branch mainline in revision 447.
  • Revision ID: jelmer@samba.org-20080309134752-syf9kwzy6e919jhj
Add note about python-nautilus requiring a libpythonXX.so symlink.

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