/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
329 by Jelmer Vernooij
Make broken_line_length setting from higher up.
94
        gobject.idle_add(self.populate, start, maxnum, 
95
                         broken_line_length)
316 by Daniel Schierbeck
Removed viz.TreeView.set_branch()
96
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.
97
        self.connect("destroy", lambda x: self.branch.unlock())
98
347 by Daniel Schierbeck
Added property getter and setter method to the treeview.
99
    def do_get_property(self, property):
100
        if property.name == 'revno-column-visible':
352 by Daniel Schierbeck
Added branch property to treeview.
101
            return self.revno_column.get_visible()
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
102
        elif property.name == 'date-column-visible':
103
            return self.date_column.get_visible()
352 by Daniel Schierbeck
Added branch property to treeview.
104
        elif property.name == 'branch':
105
            return self.branch
356 by Daniel Schierbeck
Made revision a property on TreeView.
106
        elif property.name == 'revision':
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
107
            return self.model.get_value(self.iter, treemodel.REVISION)
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
108
        elif property.name == 'revision-number':
109
            return self.model.get_value(self.iter, treemodel.REVNO)
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
110
        elif property.name == 'children':
111
            return self.model.get_value(self.iter, treemodel.CHILDREN)
112
        elif property.name == 'parents':
113
            return self.model.get_value(self.iter, treemodel.PARENTS)
347 by Daniel Schierbeck
Added property getter and setter method to the treeview.
114
        else:
115
            raise AttributeError, 'unknown property %s' % property.name
116
117
    def do_set_property(self, property, value):
118
        if property.name == 'revno-column-visible':
119
            self.revno_column.set_visible(value)
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
120
        elif property.name == 'date-column-visible':
121
            self.date_column.set_visible(value)
352 by Daniel Schierbeck
Added branch property to treeview.
122
        elif property.name == 'branch':
123
            self.branch = value
356 by Daniel Schierbeck
Made revision a property on TreeView.
124
        elif property.name == 'revision':
125
            self.set_revision_id(value.revision_id)
347 by Daniel Schierbeck
Added property getter and setter method to the treeview.
126
        else:
127
            raise AttributeError, 'unknown property %s' % property.name
128
303 by Daniel Schierbeck
Made basic signaling work.
129
    def get_revision(self):
322 by Jelmer Vernooij
Add docstrings.
130
        """Return revision id of currently selected revision, or None."""
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
131
        return self.get_property('revision')
132
133
    def set_revision(self, revision):
134
        self.set_property('revision', revision)
303 by Daniel Schierbeck
Made basic signaling work.
135
356 by Daniel Schierbeck
Made revision a property on TreeView.
136
    def set_revision_id(self, revid):
322 by Jelmer Vernooij
Add docstrings.
137
        """Change the currently selected revision.
138
139
        :param revid: Revision id of revision to display.
140
        """
305 by Daniel Schierbeck
Moved revision selection to the new view.
141
        self.treeview.set_cursor(self.index[revid])
142
        self.treeview.grab_focus()
143
303 by Daniel Schierbeck
Made basic signaling work.
144
    def get_children(self):
322 by Jelmer Vernooij
Add docstrings.
145
        """Return the children of the currently selected revision.
146
147
        :return: list of revision ids.
148
        """
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
149
        return self.get_property('children')
303 by Daniel Schierbeck
Made basic signaling work.
150
151
    def get_parents(self):
322 by Jelmer Vernooij
Add docstrings.
152
        """Return the parents of the currently selected revision.
153
154
        :return: list of revision ids.
155
        """
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
156
        return self.get_property('parents')
316 by Daniel Schierbeck
Removed viz.TreeView.set_branch()
157
        
304 by Daniel Schierbeck
Made forward/back buttons work again.
158
    def back(self):
322 by Jelmer Vernooij
Add docstrings.
159
        """Signal handler for the Back button."""
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
160
        parents = self.get_parents()
304 by Daniel Schierbeck
Made forward/back buttons work again.
161
        if not len(parents):
162
            return
163
164
        for parent_id in parents:
165
            parent_index = self.index[parent_id]
166
            parent = self.model[parent_index][treemodel.REVISION]
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
167
            if same_branch(self.get_revision(), parent):
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
168
                self.set_revision(parent)
304 by Daniel Schierbeck
Made forward/back buttons work again.
169
                break
170
        else:
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
171
            self.set_revision_id(parents[0])
304 by Daniel Schierbeck
Made forward/back buttons work again.
172
173
    def forward(self):
322 by Jelmer Vernooij
Add docstrings.
174
        """Signal handler for the Forward button."""
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
175
        children = self.get_children()
304 by Daniel Schierbeck
Made forward/back buttons work again.
176
        if not len(children):
177
            return
178
179
        for child_id in children:
180
            child_index = self.index[child_id]
181
            child = self.model[child_index][treemodel.REVISION]
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
182
            if same_branch(child, self.get_revision()):
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
183
                self.set_revision(child)
304 by Daniel Schierbeck
Made forward/back buttons work again.
184
                break
185
        else:
395.1.2 by Daniel Schierbeck
Added revision-number property to the TreeView.
186
            self.set_revision_id(children[0])
304 by Daniel Schierbeck
Made forward/back buttons work again.
187
329 by Jelmer Vernooij
Make broken_line_length setting from higher up.
188
    def populate(self, start, maxnum, broken_line_length=None):
322 by Jelmer Vernooij
Add docstrings.
189
        """Fill the treeview with contents.
190
191
        :param start: Revision id of revision to start with.
192
        :param maxnum: Maximum number of revisions to display, or None 
193
                       for no limit.
329 by Jelmer Vernooij
Make broken_line_length setting from higher up.
194
        :param broken_line_length: After how much lines branches \
195
                       should be broken.
322 by Jelmer Vernooij
Add docstrings.
196
        """
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.
197
        self.branch.lock_read()
198
        (linegraphdata, index, columns_len) = linegraph(self.branch.repository,
199
                                                        start,
200
                                                        maxnum, 
201
                                                        broken_line_length)
202
203
        self.model = TreeModel(self.branch.repository, linegraphdata)
204
        self.graph_cell.columns_len = columns_len
205
        width = self.graph_cell.get_size(self.treeview)[2]
206
        self.graph_column.set_fixed_width(width)
207
        self.graph_column.set_max_width(width)
208
        self.index = index
209
        self.treeview.set_model(self.model)
210
        self.treeview.set_cursor(0)
211
        self.emit('revisions-loaded')
304 by Daniel Schierbeck
Made forward/back buttons work again.
212
213
        return False
214
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
215
    def show_diff(self, revid=None, parentid=None):
306 by Daniel Schierbeck
Made clicking a row work again.
216
        """Open a new window to show a diff between the given revisions."""
217
        from bzrlib.plugins.gtk.diff import DiffWindow
218
        window = DiffWindow(parent=self)
330.4.1 by Daniel Schierbeck
Made it possible to view the diff of an orphant revision.
219
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
220
        parents = self.get_parents()
221
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
222
        if revid is None:
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
223
            revid = self.get_revision().revision_id
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
224
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
225
            if parentid is None and len(parents) > 0:
226
                parentid = parents[0]
330.4.3 by Daniel Schierbeck
Switched to using NULL_REVISION instead of None.
227
228
        if parentid is None:
229
            parentid = NULL_REVISION
230
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
231
        rev_tree    = self.branch.repository.revision_tree(revid)
232
        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.
233
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
234
        description = revid + " - " + self.branch.nick
306 by Daniel Schierbeck
Made clicking a row work again.
235
        window.set_diff(description, rev_tree, parent_tree)
236
        window.show()
237
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
238
    def construct_treeview(self):
239
        self.treeview = gtk.TreeView()
240
241
        self.treeview.set_rules_hint(True)
330.1.2 by Daniel Schierbeck
Replaced literal column index with equivalent symbol.
242
        self.treeview.set_search_column(treemodel.REVNO)
330.5.2 by Szilveszter Farkas (Phanatic)
Fixed set_tooltip_column() related issue.
243
        
330.5.5 by Szilveszter Farkas (Phanatic)
Use better fix by John.
244
        # Fix old PyGTK bug - by JAM
245
        set_tooltip = getattr(self.treeview, 'set_tooltip_column', None)
246
        if set_tooltip is not None:
247
            set_tooltip(treemodel.MESSAGE)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
248
391 by Daniel Schierbeck
Moved away from the changed signal on the treeview.
249
        self.treeview.connect("cursor-changed",
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
250
                self._on_selection_changed)
251
252
        self.treeview.connect("row-activated", 
253
                self._on_revision_activated)
254
255
        self.treeview.connect("button-release-event", 
256
                self._on_revision_selected)
257
258
        self.treeview.set_property('fixed-height-mode', True)
259
260
        self.add(self.treeview)
261
        self.treeview.show()
262
263
        cell = gtk.CellRendererText()
264
        cell.set_property("width-chars", 15)
265
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
345 by Daniel Schierbeck
Made treeview columns instance variables.
266
        self.revno_column = gtk.TreeViewColumn("Revision No")
267
        self.revno_column.set_resizable(True)
268
        self.revno_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
269
        self.revno_column.set_fixed_width(cell.get_size(self.treeview)[2])
270
        self.revno_column.pack_start(cell, expand=True)
271
        self.revno_column.add_attribute(cell, "text", treemodel.REVNO)
272
        self.treeview.append_column(self.revno_column)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
273
274
        self.graph_cell = CellRendererGraph()
275
        self.graph_column = gtk.TreeViewColumn()
276
        self.graph_column.set_resizable(True)
277
        self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
278
        self.graph_column.pack_start(self.graph_cell, expand=False)
279
        self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
280
        self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
281
        self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
282
        self.treeview.append_column(self.graph_column)
283
284
        cell = gtk.CellRendererText()
285
        cell.set_property("width-chars", 65)
286
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
345 by Daniel Schierbeck
Made treeview columns instance variables.
287
        self.msg_column = gtk.TreeViewColumn("Message")
288
        self.msg_column.set_resizable(True)
289
        self.msg_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
290
        self.msg_column.set_fixed_width(cell.get_size(self.treeview)[2])
291
        self.msg_column.pack_start(cell, expand=True)
292
        self.msg_column.add_attribute(cell, "text", treemodel.MESSAGE)
293
        self.treeview.append_column(self.msg_column)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
294
295
        cell = gtk.CellRendererText()
296
        cell.set_property("width-chars", 15)
297
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
345 by Daniel Schierbeck
Made treeview columns instance variables.
298
        self.committer_column = gtk.TreeViewColumn("Committer")
299
        self.committer_column.set_resizable(True)
300
        self.committer_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
301
        self.committer_column.set_fixed_width(cell.get_size(self.treeview)[2])
302
        self.committer_column.pack_start(cell, expand=True)
303
        self.committer_column.add_attribute(cell, "text", treemodel.COMMITER)
304
        self.treeview.append_column(self.committer_column)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
305
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
306
        cell = gtk.CellRendererText()
359 by Daniel Schierbeck
Simplified date format.
307
        cell.set_property("width-chars", 20)
357 by Daniel Schierbeck
Added support for showing the date column in the viz.
308
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
309
        self.date_column = gtk.TreeViewColumn("Date")
310
        self.date_column.set_visible(False)
311
        self.date_column.set_resizable(True)
312
        self.date_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
313
        self.date_column.set_fixed_width(cell.get_size(self.treeview)[2])
314
        self.date_column.pack_start(cell, expand=True)
315
        self.date_column.add_attribute(cell, "text", treemodel.TIMESTAMP)
316
        self.treeview.append_column(self.date_column)
317
391 by Daniel Schierbeck
Moved away from the changed signal on the treeview.
318
    def _on_selection_changed(self, treeview):
303 by Daniel Schierbeck
Made basic signaling work.
319
        """callback for when the treeview changes."""
391 by Daniel Schierbeck
Moved away from the changed signal on the treeview.
320
        (path, focus) = treeview.get_cursor()
321
        if path is not None:
395.1.1 by Daniel Schierbeck
Cleaned up code in the TreeView, removing instance variables.
322
            self.iter = self.model.get_iter(path)
310 by Daniel Schierbeck
Moved to using custom signal for handling revision selections.
323
            self.emit('revision-selected')
324
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
325
    def _on_revision_selected(self, widget, event):
326
        from bzrlib.plugins.gtk.revisionmenu import RevisionPopupMenu
327
        if event.button == 3:
328
            menu = RevisionPopupMenu(self.branch.repository, 
312 by Daniel Schierbeck
Made right-clicking work again.
329
                [self.get_revision().revision_id],
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
330
                self.branch)
331
            menu.popup(None, None, None, event.button, event.get_time())
332
333
    def _on_revision_activated(self, widget, path, col):
334
        # TODO: more than one parent
335
        """Callback for when a treeview row gets activated."""
336
        revision_id = self.model[path][treemodel.REVID]
337
        parents = self.model[path][treemodel.PARENTS]
330.4.1 by Daniel Schierbeck
Made it possible to view the diff of an orphant revision.
338
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
339
        if len(parents) == 0:
330.4.1 by Daniel Schierbeck
Made it possible to view the diff of an orphant revision.
340
            parent_id = None
341
        else:
342
            parent_id = parents[0]
343
364 by Daniel Schierbeck
Simplified TreeView.show_diff() and made the Revision -> View Changes menu item work.
344
        self.show_diff(revision_id, parent_id)
301 by Daniel Schierbeck
Initial work on extracting the TreeView from the branch window.
345
        self.treeview.grab_focus()