/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
1
# -*- coding: UTF-8 -*-
2
"""Revision history view.
3
4
"""
5
6
__copyright__ = "Copyright © 2005 Canonical Ltd."
7
__author__    = "Daniel Schierbeck <daniel.schierbeck@gmail.com>"
8
314 by Daniel Schierbeck
Moved branch locking into treeview.
9
import sys
10
import string
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
11
import gtk
12
import gobject
13
import pango
14
import re
303 by Daniel Schierbeck
Made basic signaling work.
15
import treemodel
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
16
17
from linegraph import linegraph, same_branch
18
from graphcell import CellRendererGraph
19
from treemodel import TreeModel
330.4.3 by Daniel Schierbeck
Switched to using NULL_REVISION instead of None.
20
from bzrlib.revision import NULL_REVISION
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
21
303 by Daniel Schierbeck
Made basic signaling work.
22
class TreeView(gtk.ScrolledWindow):
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
23
346 by Daniel Schierbeck
Added revno-column-visible property to treeview.
24
    __gproperties__ = {
352 by Daniel Schierbeck
Added branch property to treeview.
25
        'branch': (gobject.TYPE_PYOBJECT,
26
                   'Branch',
27
                   'The Bazaar branch being visualized',
28
                   gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE),
29
356 by Daniel Schierbeck
Made revision a property on TreeView.
30
        'revision': (gobject.TYPE_PYOBJECT,
31
                     'Revision',
32
                     'The currently selected revision',
33
                     gobject.PARAM_READWRITE),
34
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
35
        'revision-number': (gobject.TYPE_STRING,
36
                            'Revision number',
37
                            'The number of the selected revision',
38
                            '',
39
                            gobject.PARAM_READABLE),
40
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
41
        'children': (gobject.TYPE_PYOBJECT,
42
                     'Child revisions',
43
                     'Children of the currently selected revision',
44
                     gobject.PARAM_READABLE),
45
46
        'parents': (gobject.TYPE_PYOBJECT,
47
                    'Parent revisions',
48
                    'Parents to the currently selected revision',
49
                    gobject.PARAM_READABLE),
50
346 by Daniel Schierbeck
Added revno-column-visible property to treeview.
51
        'revno-column-visible': (gobject.TYPE_BOOLEAN,
52
                                 'Revision number',
53
                                 'Show revision number column',
54
                                 True,
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
55
                                 gobject.PARAM_READWRITE),
56
57
        'date-column-visible': (gobject.TYPE_BOOLEAN,
360 by Daniel Schierbeck
Fixed wrong text in date property.
58
                                 'Date',
59
                                 'Show date column',
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
60
                                 False,
346 by Daniel Schierbeck
Added revno-column-visible property to treeview.
61
                                 gobject.PARAM_READWRITE)
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
62
346 by Daniel Schierbeck
Added revno-column-visible property to treeview.
63
    }
64
307 by Daniel Schierbeck
Made load notification work again.
65
    __gsignals__ = {
345 by Daniel Schierbeck
Made treeview columns instance variables.
66
        'revisions-loaded': (gobject.SIGNAL_RUN_FIRST, 
67
                             gobject.TYPE_NONE,
68
                             ()),
69
        'revision-selected': (gobject.SIGNAL_RUN_FIRST,
70
                              gobject.TYPE_NONE,
71
                              ())
307 by Daniel Schierbeck
Made load notification work again.
72
    }
73
329 by Jelmer Vernooij
Make broken_line_length setting from higher up.
74
    def __init__(self, branch, start, maxnum, broken_line_length=None):
322 by Jelmer Vernooij
Add docstrings.
75
        """Create a new TreeView.
76
77
        :param branch: Branch object for branch to show.
78
        :param start: Revision id of top revision.
79
        :param maxnum: Maximum number of revisions to display, 
80
                       None for no limit.
329 by Jelmer Vernooij
Make broken_line_length setting from higher up.
81
        :param broken_line_length: After how much lines to break 
82
                                   branches.
322 by Jelmer Vernooij
Add docstrings.
83
        """
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
84
        gtk.ScrolledWindow.__init__(self)
85
303 by Daniel Schierbeck
Made basic signaling work.
86
        self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
87
        self.set_shadow_type(gtk.SHADOW_IN)
88
303 by Daniel Schierbeck
Made basic signaling work.
89
        self.construct_treeview()
90
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
91
        self.iter   = None
316 by Daniel Schierbeck
Removed viz.TreeView.set_branch()
92
        self.branch = branch
93
401.1.1 by Daniel Schierbeck
Added update() method to TreeView.
94
        self.start = start
95
        self.maxnum = maxnum
96
        self.broken_line_length = broken_line_length
97
98
        gobject.idle_add(self.populate)
316 by Daniel Schierbeck
Removed viz.TreeView.set_branch()
99
392 by Daniel Schierbeck
Fixed performance issue by only releasing the branch lock when the treeview is destroyed. This will surely fuck up the tagging code.
100
        self.connect("destroy", lambda x: self.branch.unlock())
101
347 by Daniel Schierbeck
Added property getter and setter method to the treeview.
102
    def do_get_property(self, property):
103
        if property.name == 'revno-column-visible':
352 by Daniel Schierbeck
Added branch property to treeview.
104
            return self.revno_column.get_visible()
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
105
        elif property.name == 'date-column-visible':
106
            return self.date_column.get_visible()
352 by Daniel Schierbeck
Added branch property to treeview.
107
        elif property.name == 'branch':
108
            return self.branch
356 by Daniel Schierbeck
Made revision a property on TreeView.
109
        elif property.name == 'revision':
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
110
            return self.model.get_value(self.iter, treemodel.REVISION)
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
111
        elif property.name == 'revision-number':
112
            return self.model.get_value(self.iter, treemodel.REVNO)
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
113
        elif property.name == 'children':
114
            return self.model.get_value(self.iter, treemodel.CHILDREN)
115
        elif property.name == 'parents':
116
            return self.model.get_value(self.iter, treemodel.PARENTS)
347 by Daniel Schierbeck
Added property getter and setter method to the treeview.
117
        else:
118
            raise AttributeError, 'unknown property %s' % property.name
119
120
    def do_set_property(self, property, value):
121
        if property.name == 'revno-column-visible':
122
            self.revno_column.set_visible(value)
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
123
        elif property.name == 'date-column-visible':
124
            self.date_column.set_visible(value)
352 by Daniel Schierbeck
Added branch property to treeview.
125
        elif property.name == 'branch':
126
            self.branch = value
356 by Daniel Schierbeck
Made revision a property on TreeView.
127
        elif property.name == 'revision':
128
            self.set_revision_id(value.revision_id)
347 by Daniel Schierbeck
Added property getter and setter method to the treeview.
129
        else:
130
            raise AttributeError, 'unknown property %s' % property.name
131
303 by Daniel Schierbeck
Made basic signaling work.
132
    def get_revision(self):
322 by Jelmer Vernooij
Add docstrings.
133
        """Return revision id of currently selected revision, or None."""
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
134
        return self.get_property('revision')
135
136
    def set_revision(self, revision):
137
        self.set_property('revision', revision)
303 by Daniel Schierbeck
Made basic signaling work.
138
356 by Daniel Schierbeck
Made revision a property on TreeView.
139
    def set_revision_id(self, revid):
322 by Jelmer Vernooij
Add docstrings.
140
        """Change the currently selected revision.
141
142
        :param revid: Revision id of revision to display.
143
        """
305 by Daniel Schierbeck
Moved revision selection to the new view.
144
        self.treeview.set_cursor(self.index[revid])
145
        self.treeview.grab_focus()
146
303 by Daniel Schierbeck
Made basic signaling work.
147
    def get_children(self):
322 by Jelmer Vernooij
Add docstrings.
148
        """Return the children of the currently selected revision.
149
150
        :return: list of revision ids.
151
        """
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
152
        return self.get_property('children')
303 by Daniel Schierbeck
Made basic signaling work.
153
154
    def get_parents(self):
322 by Jelmer Vernooij
Add docstrings.
155
        """Return the parents of the currently selected revision.
156
157
        :return: list of revision ids.
158
        """
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
159
        return self.get_property('parents')
316 by Daniel Schierbeck
Removed viz.TreeView.set_branch()
160
        
401.1.1 by Daniel Schierbeck
Added update() method to TreeView.
161
    def update(self):
162
        self.iter = None
163
        gobject.idle_add(self.populate)
164
304 by Daniel Schierbeck
Made forward/back buttons work again.
165
    def back(self):
322 by Jelmer Vernooij
Add docstrings.
166
        """Signal handler for the Back button."""
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
167
        parents = self.get_parents()
304 by Daniel Schierbeck
Made forward/back buttons work again.
168
        if not len(parents):
169
            return
170
171
        for parent_id in parents:
172
            parent_index = self.index[parent_id]
173
            parent = self.model[parent_index][treemodel.REVISION]
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
174
            if same_branch(self.get_revision(), parent):
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
175
                self.set_revision(parent)
304 by Daniel Schierbeck
Made forward/back buttons work again.
176
                break
177
        else:
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
178
            self.set_revision_id(parents[0])
304 by Daniel Schierbeck
Made forward/back buttons work again.
179
180
    def forward(self):
322 by Jelmer Vernooij
Add docstrings.
181
        """Signal handler for the Forward button."""
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
182
        children = self.get_children()
304 by Daniel Schierbeck
Made forward/back buttons work again.
183
        if not len(children):
184
            return
185
186
        for child_id in children:
187
            child_index = self.index[child_id]
188
            child = self.model[child_index][treemodel.REVISION]
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
189
            if same_branch(child, self.get_revision()):
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
190
                self.set_revision(child)
304 by Daniel Schierbeck
Made forward/back buttons work again.
191
                break
192
        else:
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
193
            self.set_revision_id(children[0])
304 by Daniel Schierbeck
Made forward/back buttons work again.
194
401.1.1 by Daniel Schierbeck
Added update() method to TreeView.
195
    def populate(self):
322 by Jelmer Vernooij
Add docstrings.
196
        """Fill the treeview with contents.
197
198
        :param start: Revision id of revision to start with.
199
        :param maxnum: Maximum number of revisions to display, or None 
200
                       for no limit.
329 by Jelmer Vernooij
Make broken_line_length setting from higher up.
201
        :param broken_line_length: After how much lines branches \
202
                       should be broken.
322 by Jelmer Vernooij
Add docstrings.
203
        """
392 by Daniel Schierbeck
Fixed performance issue by only releasing the branch lock when the treeview is destroyed. This will surely fuck up the tagging code.
204
        self.branch.lock_read()
205
        (linegraphdata, index, columns_len) = linegraph(self.branch.repository,
401.1.1 by Daniel Schierbeck
Added update() method to TreeView.
206
                                                        self.start,
207
                                                        self.maxnum, 
208
                                                        self.broken_line_length)
392 by Daniel Schierbeck
Fixed performance issue by only releasing the branch lock when the treeview is destroyed. This will surely fuck up the tagging code.
209
210
        self.model = TreeModel(self.branch.repository, linegraphdata)
211
        self.graph_cell.columns_len = columns_len
212
        width = self.graph_cell.get_size(self.treeview)[2]
213
        self.graph_column.set_fixed_width(width)
214
        self.graph_column.set_max_width(width)
215
        self.index = index
216
        self.treeview.set_model(self.model)
217
        self.treeview.set_cursor(0)
218
        self.emit('revisions-loaded')
304 by Daniel Schierbeck
Made forward/back buttons work again.
219
220
        return False
221
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
222
    def show_diff(self, revid=None, parentid=None):
306 by Daniel Schierbeck
Made clicking a row work again.
223
        """Open a new window to show a diff between the given revisions."""
224
        from bzrlib.plugins.gtk.diff import DiffWindow
225
        window = DiffWindow(parent=self)
330.4.1 by Daniel Schierbeck
Made it possible to view the diff of an orphant revision.
226
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
227
        parents = self.get_parents()
228
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
229
        if revid is None:
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
230
            revid = self.get_revision().revision_id
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
231
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
232
            if parentid is None and len(parents) > 0:
233
                parentid = parents[0]
330.4.3 by Daniel Schierbeck
Switched to using NULL_REVISION instead of None.
234
235
        if parentid is None:
236
            parentid = NULL_REVISION
237
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
238
        rev_tree    = self.branch.repository.revision_tree(revid)
239
        parent_tree = self.branch.repository.revision_tree(parentid)
330.4.1 by Daniel Schierbeck
Made it possible to view the diff of an orphant revision.
240
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
241
        description = revid + " - " + self.branch.nick
306 by Daniel Schierbeck
Made clicking a row work again.
242
        window.set_diff(description, rev_tree, parent_tree)
243
        window.show()
244
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
245
    def construct_treeview(self):
246
        self.treeview = gtk.TreeView()
247
248
        self.treeview.set_rules_hint(True)
330.1.2 by Daniel Schierbeck
Replaced literal column index with equivalent symbol.
249
        self.treeview.set_search_column(treemodel.REVNO)
330.5.2 by Szilveszter Farkas (Phanatic)
Fixed set_tooltip_column() related issue.
250
        
330.5.5 by Szilveszter Farkas (Phanatic)
Use better fix by John.
251
        # Fix old PyGTK bug - by JAM
252
        set_tooltip = getattr(self.treeview, 'set_tooltip_column', None)
253
        if set_tooltip is not None:
254
            set_tooltip(treemodel.MESSAGE)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
255
391 by Daniel Schierbeck
Moved away from the changed signal on the treeview.
256
        self.treeview.connect("cursor-changed",
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
257
                self._on_selection_changed)
258
259
        self.treeview.connect("row-activated", 
260
                self._on_revision_activated)
261
262
        self.treeview.connect("button-release-event", 
263
                self._on_revision_selected)
264
265
        self.treeview.set_property('fixed-height-mode', True)
266
267
        self.add(self.treeview)
268
        self.treeview.show()
269
270
        cell = gtk.CellRendererText()
271
        cell.set_property("width-chars", 15)
272
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
345 by Daniel Schierbeck
Made treeview columns instance variables.
273
        self.revno_column = gtk.TreeViewColumn("Revision No")
274
        self.revno_column.set_resizable(True)
275
        self.revno_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
276
        self.revno_column.set_fixed_width(cell.get_size(self.treeview)[2])
277
        self.revno_column.pack_start(cell, expand=True)
278
        self.revno_column.add_attribute(cell, "text", treemodel.REVNO)
279
        self.treeview.append_column(self.revno_column)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
280
281
        self.graph_cell = CellRendererGraph()
282
        self.graph_column = gtk.TreeViewColumn()
283
        self.graph_column.set_resizable(True)
284
        self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
285
        self.graph_column.pack_start(self.graph_cell, expand=False)
286
        self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
287
        self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
288
        self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
289
        self.treeview.append_column(self.graph_column)
290
291
        cell = gtk.CellRendererText()
292
        cell.set_property("width-chars", 65)
293
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
345 by Daniel Schierbeck
Made treeview columns instance variables.
294
        self.msg_column = gtk.TreeViewColumn("Message")
295
        self.msg_column.set_resizable(True)
296
        self.msg_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
297
        self.msg_column.set_fixed_width(cell.get_size(self.treeview)[2])
298
        self.msg_column.pack_start(cell, expand=True)
299
        self.msg_column.add_attribute(cell, "text", treemodel.MESSAGE)
300
        self.treeview.append_column(self.msg_column)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
301
302
        cell = gtk.CellRendererText()
303
        cell.set_property("width-chars", 15)
304
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
345 by Daniel Schierbeck
Made treeview columns instance variables.
305
        self.committer_column = gtk.TreeViewColumn("Committer")
306
        self.committer_column.set_resizable(True)
307
        self.committer_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
308
        self.committer_column.set_fixed_width(cell.get_size(self.treeview)[2])
309
        self.committer_column.pack_start(cell, expand=True)
310
        self.committer_column.add_attribute(cell, "text", treemodel.COMMITER)
311
        self.treeview.append_column(self.committer_column)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
312
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
313
        cell = gtk.CellRendererText()
359 by Daniel Schierbeck
Simplified date format.
314
        cell.set_property("width-chars", 20)
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
315
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
316
        self.date_column = gtk.TreeViewColumn("Date")
317
        self.date_column.set_visible(False)
318
        self.date_column.set_resizable(True)
319
        self.date_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
320
        self.date_column.set_fixed_width(cell.get_size(self.treeview)[2])
321
        self.date_column.pack_start(cell, expand=True)
322
        self.date_column.add_attribute(cell, "text", treemodel.TIMESTAMP)
323
        self.treeview.append_column(self.date_column)
324
391 by Daniel Schierbeck
Moved away from the changed signal on the treeview.
325
    def _on_selection_changed(self, treeview):
303 by Daniel Schierbeck
Made basic signaling work.
326
        """callback for when the treeview changes."""
391 by Daniel Schierbeck
Moved away from the changed signal on the treeview.
327
        (path, focus) = treeview.get_cursor()
328
        if path is not None:
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
329
            self.iter = self.model.get_iter(path)
310 by Daniel Schierbeck
Moved to using custom signal for handling revision selections.
330
            self.emit('revision-selected')
331
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
332
    def _on_revision_selected(self, widget, event):
333
        from bzrlib.plugins.gtk.revisionmenu import RevisionPopupMenu
334
        if event.button == 3:
335
            menu = RevisionPopupMenu(self.branch.repository, 
312 by Daniel Schierbeck
Made right-clicking work again.
336
                [self.get_revision().revision_id],
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
337
                self.branch)
338
            menu.popup(None, None, None, event.button, event.get_time())
339
340
    def _on_revision_activated(self, widget, path, col):
341
        # TODO: more than one parent
342
        """Callback for when a treeview row gets activated."""
343
        revision_id = self.model[path][treemodel.REVID]
344
        parents = self.model[path][treemodel.PARENTS]
330.4.1 by Daniel Schierbeck
Made it possible to view the diff of an orphant revision.
345
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
346
        if len(parents) == 0:
330.4.1 by Daniel Schierbeck
Made it possible to view the diff of an orphant revision.
347
            parent_id = None
348
        else:
349
            parent_id = parents[0]
350
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
351
        self.show_diff(revision_id, parent_id)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
352
        self.treeview.grab_focus()