/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 branchview/treeview.py

  • Committer: Vincent Ladeuil
  • Date: 2009-06-15 06:53:53 UTC
  • mto: This revision was merged to the branch mainline in revision 649.
  • Revision ID: v.ladeuil+lp@free.fr-20090615065353-nxyyk6lj8te4ciiw
Fix refresh warnings and progress widget usage.

* branchview/treeview.py:
(TreeView.__init__): Make the proress widget an attribute. Don't
keep an GtkIter, they are not persistent, keep the path instead.
(TreeView.do_get_property): Convert path to iter when needed.
(TreeView._on_selection_changed): Memorize path, not iter.
(TreeView.populate): Use our own widget if we can.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
__copyright__ = "Copyright © 2005 Canonical Ltd."
7
7
__author__    = "Daniel Schierbeck <daniel.schierbeck@gmail.com>"
8
8
 
9
 
from gi.repository import Gtk
10
 
from gi.repository import GObject
11
 
from gi.repository import Pango
12
 
 
 
9
import sys
 
10
import string
 
11
import gtk
 
12
import gobject
 
13
import pango
 
14
import re
 
15
import treemodel
13
16
from bzrlib import ui
 
17
 
 
18
from bzrlib.plugins.gtk import _i18n
 
19
from bzrlib.plugins.gtk.ui import GtkProgressBar, ProgressPanel
 
20
from linegraph import linegraph, same_branch
 
21
from graphcell import CellRendererGraph
 
22
from treemodel import TreeModel
14
23
from bzrlib.revision import NULL_REVISION
15
24
 
16
 
from bzrlib.plugins.gtk import lock
17
 
from bzrlib.plugins.gtk.ui import ProgressPanel
18
 
from bzrlib.plugins.gtk.branchview import treemodel
19
 
from bzrlib.plugins.gtk.branchview.linegraph import linegraph, same_branch
20
 
from bzrlib.plugins.gtk.branchview.graphcell import CellRendererGraph
21
 
 
22
 
 
23
 
class TreeView(Gtk.VBox):
 
25
 
 
26
class TreeView(gtk.VBox):
24
27
 
25
28
    __gproperties__ = {
26
 
        'branch': (GObject.TYPE_PYOBJECT,
 
29
        'branch': (gobject.TYPE_PYOBJECT,
27
30
                   'Branch',
28
31
                   'The Bazaar branch being visualized',
29
 
                   GObject.PARAM_CONSTRUCT_ONLY | GObject.PARAM_WRITABLE),
 
32
                   gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE),
30
33
 
31
 
        'revision': (GObject.TYPE_PYOBJECT,
 
34
        'revision': (gobject.TYPE_PYOBJECT,
32
35
                     'Revision',
33
36
                     'The currently selected revision',
34
 
                     GObject.PARAM_READWRITE),
 
37
                     gobject.PARAM_READWRITE),
35
38
 
36
 
        'revision-number': (GObject.TYPE_STRING,
 
39
        'revision-number': (gobject.TYPE_STRING,
37
40
                            'Revision number',
38
41
                            'The number of the selected revision',
39
42
                            '',
40
 
                            GObject.PARAM_READABLE),
 
43
                            gobject.PARAM_READABLE),
41
44
 
42
 
        'children': (GObject.TYPE_PYOBJECT,
 
45
        'children': (gobject.TYPE_PYOBJECT,
43
46
                     'Child revisions',
44
47
                     'Children of the currently selected revision',
45
 
                     GObject.PARAM_READABLE),
 
48
                     gobject.PARAM_READABLE),
46
49
 
47
 
        'parents': (GObject.TYPE_PYOBJECT,
 
50
        'parents': (gobject.TYPE_PYOBJECT,
48
51
                    'Parent revisions',
49
52
                    'Parents to the currently selected revision',
50
 
                    GObject.PARAM_READABLE),
 
53
                    gobject.PARAM_READABLE),
51
54
 
52
 
        'revno-column-visible': (GObject.TYPE_BOOLEAN,
 
55
        'revno-column-visible': (gobject.TYPE_BOOLEAN,
53
56
                                 'Revision number column',
54
57
                                 'Show revision number column',
55
58
                                 True,
56
 
                                 GObject.PARAM_READWRITE),
 
59
                                 gobject.PARAM_READWRITE),
57
60
 
58
 
        'graph-column-visible': (GObject.TYPE_BOOLEAN,
 
61
        'graph-column-visible': (gobject.TYPE_BOOLEAN,
59
62
                                 'Graph column',
60
63
                                 'Show graph column',
61
64
                                 True,
62
 
                                 GObject.PARAM_READWRITE),
 
65
                                 gobject.PARAM_READWRITE),
63
66
 
64
 
        'date-column-visible': (GObject.TYPE_BOOLEAN,
 
67
        'date-column-visible': (gobject.TYPE_BOOLEAN,
65
68
                                 'Date',
66
69
                                 'Show date column',
67
70
                                 False,
68
 
                                 GObject.PARAM_READWRITE),
 
71
                                 gobject.PARAM_READWRITE),
69
72
 
70
 
        'compact': (GObject.TYPE_BOOLEAN,
 
73
        'compact': (gobject.TYPE_BOOLEAN,
71
74
                    'Compact view',
72
75
                    'Break ancestry lines to save space',
73
76
                    True,
74
 
                    GObject.PARAM_CONSTRUCT | GObject.PARAM_READWRITE),
 
77
                    gobject.PARAM_CONSTRUCT | gobject.PARAM_READWRITE),
75
78
 
76
 
        'mainline-only': (GObject.TYPE_BOOLEAN,
 
79
        'mainline-only': (gobject.TYPE_BOOLEAN,
77
80
                    'Mainline only',
78
81
                    'Only show the mainline history.',
79
82
                    False,
80
 
                    GObject.PARAM_CONSTRUCT | GObject.PARAM_READWRITE),
 
83
                    gobject.PARAM_CONSTRUCT | gobject.PARAM_READWRITE),
81
84
 
82
85
    }
83
86
 
84
87
    __gsignals__ = {
85
 
        'revision-selected': (GObject.SignalFlags.RUN_FIRST,
86
 
                              None,
 
88
        'revision-selected': (gobject.SIGNAL_RUN_FIRST,
 
89
                              gobject.TYPE_NONE,
87
90
                              ()),
88
 
        'revision-activated': (GObject.SignalFlags.RUN_FIRST,
89
 
                              None,
90
 
                              (GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT)),
91
 
        'tag-added': (GObject.SignalFlags.RUN_FIRST,
92
 
                              None,
93
 
                              (GObject.TYPE_STRING, GObject.TYPE_STRING)),
94
 
        'refreshed': (GObject.SignalFlags.RUN_FIRST, None,
 
91
        'revision-activated': (gobject.SIGNAL_RUN_FIRST,
 
92
                              gobject.TYPE_NONE,
 
93
                              (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
 
94
        'tag-added': (gobject.SIGNAL_RUN_FIRST,
 
95
                              gobject.TYPE_NONE,
 
96
                              (gobject.TYPE_STRING, gobject.TYPE_STRING)),
 
97
        'refreshed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
95
98
                              ())
96
99
    }
97
100
 
105
108
        :param broken_line_length: After how much lines to break 
106
109
                                   branches.
107
110
        """
108
 
        super(TreeView, self).__init__(homogeneous=False, spacing=0)
 
111
        gtk.VBox.__init__(self, spacing=0)
109
112
 
110
113
        self.progress_widget = ProgressPanel()
111
 
        self.pack_start(self.progress_widget, False, True, 0)
112
 
        if getattr(ui.ui_factory, "set_progress_bar_widget", None) is not None:
113
 
            # We'are using our own ui, let's tell it to use our widget.
114
 
            ui.ui_factory.set_progress_bar_widget(self.progress_widget)
 
114
        self.pack_start(self.progress_widget, expand=False, fill=True)
115
115
 
116
 
        self.scrolled_window = Gtk.ScrolledWindow()
117
 
        self.scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
118
 
                                        Gtk.PolicyType.AUTOMATIC)
119
 
        self.scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
 
116
        self.scrolled_window = gtk.ScrolledWindow()
 
117
        self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
 
118
                                        gtk.POLICY_AUTOMATIC)
 
119
        self.scrolled_window.set_shadow_type(gtk.SHADOW_IN)
120
120
        self.scrolled_window.show()
121
 
        self.pack_start(self.scrolled_window, True, True, 0)
 
121
        self.pack_start(self.scrolled_window, expand=True, fill=True)
122
122
 
123
123
        self.scrolled_window.add(self.construct_treeview())
124
124
 
131
131
        self.maxnum = maxnum
132
132
        self.compact = compact
133
133
 
134
 
        self.model = treemodel.BranchTreeModel(self.branch, [])
135
 
        GObject.idle_add(self.populate)
136
 
 
137
 
        self.connect("destroy", self._on_destroy)
138
 
 
139
 
    def _on_destroy(self, *ignored):
140
 
        self.branch.unlock()
141
 
        if getattr(ui.ui_factory, "set_progress_bar_widget", None) is not None:
142
 
            # We'are using our own ui, let's tell it to stop using our widget.
143
 
            ui.ui_factory.set_progress_bar_widget(None)
 
134
        gobject.idle_add(self.populate)
 
135
 
 
136
        self.connect("destroy", lambda x: self.branch.unlock())
144
137
 
145
138
    def do_get_property(self, property):
146
139
        if property.name == 'revno-column-visible':
156
149
        elif property.name == 'branch':
157
150
            return self.branch
158
151
        elif property.name == 'revision':
159
 
            if self.path is None:
160
 
                return None
161
152
            return self.model.get_value(self.model.get_iter(self.path),
162
153
                                        treemodel.REVISION)
163
154
        elif property.name == 'revision-number':
164
 
            if self.path is None:
165
 
                return None
166
155
            return self.model.get_value(self.model.get_iter(self.path),
167
156
                                        treemodel.REVNO)
168
157
        elif property.name == 'children':
169
 
            if self.path is None:
170
 
                return None
171
158
            return self.model.get_value(self.model.get_iter(self.path),
172
159
                                        treemodel.CHILDREN)
173
160
        elif property.name == 'parents':
174
 
            if self.path is None:
175
 
                return None
176
161
            return self.model.get_value(self.model.get_iter(self.path),
177
162
                                        treemodel.PARENTS)
178
163
        else:
211
196
 
212
197
        :param revid: Revision id of revision to display.
213
198
        """
214
 
        self.treeview.set_cursor(
215
 
            Gtk.TreePath(path=self.index[revid]), None, False)
 
199
        self.treeview.set_cursor(self.index[revid])
216
200
        self.treeview.grab_focus()
217
201
 
218
202
    def get_children(self):
232
216
    def add_tag(self, tag, revid=None):
233
217
        if revid is None: revid = self.revision.revision_id
234
218
 
235
 
        if lock.release(self.branch):
 
219
        try:
 
220
            self.branch.unlock()
 
221
 
236
222
            try:
237
 
                lock.acquire(self.branch, lock.WRITE)
 
223
                self.branch.lock_write()
238
224
                self.model.add_tag(tag, revid)
239
225
            finally:
240
 
                lock.release(self.branch)
241
 
 
242
 
            lock.acquire(self.branch, lock.READ)
243
 
 
244
 
            self.emit('tag-added', tag, revid)
245
 
 
 
226
                self.branch.unlock()
 
227
 
 
228
        finally:
 
229
            self.branch.lock_read()
 
230
 
 
231
        self.emit('tag-added', tag, revid)
 
232
        
246
233
    def refresh(self):
247
 
        GObject.idle_add(self.populate, self.get_revision())
 
234
        gobject.idle_add(self.populate, self.get_revision())
248
235
 
249
236
    def update(self):
250
237
        try:
260
247
    def back(self):
261
248
        """Signal handler for the Back button."""
262
249
        parents = self.get_parents()
263
 
        if not parents:
 
250
        if not len(parents):
264
251
            return
265
252
 
266
253
        for parent_id in parents:
275
262
    def forward(self):
276
263
        """Signal handler for the Forward button."""
277
264
        children = self.get_children()
278
 
        if not children:
 
265
        if not len(children):
279
266
            return
280
267
 
281
268
        for child_id in children:
308
295
                broken_line_length = 32
309
296
            else:
310
297
                broken_line_length = None
311
 
 
 
298
            
312
299
            show_graph = self.graph_column.get_visible()
313
300
 
314
301
            self.branch.lock_read()
315
 
            (linegraphdata, index, columns_len) = linegraph(
316
 
                self.branch.repository.get_graph(),
317
 
                self.start,
318
 
                self.maxnum, 
319
 
                broken_line_length,
320
 
                show_graph,
321
 
                self.mainline_only,
322
 
                self.progress_bar)
 
302
            (linegraphdata, index, columns_len) = linegraph(self.branch.repository.get_graph(),
 
303
                                                            self.start,
 
304
                                                            self.maxnum, 
 
305
                                                            broken_line_length,
 
306
                                                            show_graph,
 
307
                                                            self.mainline_only,
 
308
                                                            self.progress_bar)
323
309
 
324
 
            self.model.set_line_graph_data(linegraphdata)
 
310
            self.model = TreeModel(self.branch, linegraphdata)
325
311
            self.graph_cell.columns_len = columns_len
326
 
            width = self.graph_cell.get_preferred_width(self.treeview)[1]
 
312
            width = self.graph_cell.get_size(self.treeview)[2]
327
313
            if width > 500:
328
314
                width = 500
329
 
            elif width == 0:
330
 
                # The get_preferred_width() call got an insane value.
331
 
                width = 200
332
315
            self.graph_column.set_fixed_width(width)
333
316
            self.graph_column.set_max_width(width)
334
317
            self.index = index
335
318
            self.treeview.set_model(self.model)
336
319
 
337
320
            if not revision or revision == NULL_REVISION:
338
 
                self.treeview.set_cursor(Gtk.TreePath(path=0), None, False)
 
321
                self.treeview.set_cursor(0)
339
322
            else:
340
323
                self.set_revision(revision)
341
324
 
345
328
            self.progress_bar.finished()
346
329
 
347
330
    def construct_treeview(self):
348
 
        self.treeview = Gtk.TreeView()
 
331
        self.treeview = gtk.TreeView()
349
332
 
350
333
        self.treeview.set_rules_hint(True)
351
 
        # combined revno/summary interactive search
352
 
        #
353
 
        # the row in a treemodel is considered "matched" if a REVNO *starts*
354
 
        # from the key (that is the key is found in a REVNO at the offset 0)
355
 
        # or if a MESSAGE *contains* the key anywhere (that is, the key is
356
 
        # found case insensitively in a MESSAGE at any offset)
357
 
        def search_equal_func(model, column, key, iter, ignored):
358
 
            return (model.get_value(iter, treemodel.REVNO).find(key) != 0
359
 
                and model.get_value(iter, treemodel.MESSAGE).lower().find(key.lower()) == -1)
360
 
 
361
 
        self.treeview.set_search_equal_func(search_equal_func, None)
362
 
        self.treeview.set_enable_search(True)
363
 
 
364
 
        self.treeview.set_tooltip_column(treemodel.MESSAGE)
365
 
        self.treeview.set_headers_visible(True)
 
334
        self.treeview.set_search_column(treemodel.REVNO)
 
335
        
 
336
        # Fix old PyGTK bug - by JAM
 
337
        set_tooltip = getattr(self.treeview, 'set_tooltip_column', None)
 
338
        if set_tooltip is not None:
 
339
            set_tooltip(treemodel.MESSAGE)
366
340
 
367
341
        self._prev_cursor_path = None
368
342
        self.treeview.connect("cursor-changed",
378
352
 
379
353
        self.treeview.show()
380
354
 
381
 
        cell = Gtk.CellRendererText()
 
355
        cell = gtk.CellRendererText()
382
356
        cell.set_property("width-chars", 15)
383
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
384
 
        self.revno_column = Gtk.TreeViewColumn("Revision No")
385
 
        self.revno_column.set_resizable(True)
386
 
        self.revno_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
387
 
        self.revno_column.set_fixed_width(
388
 
            cell.get_preferred_width(self.treeview)[1])
389
 
        self.revno_column.pack_start(cell, True)
 
357
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
358
        self.revno_column = gtk.TreeViewColumn("Revision No")
 
359
        self.revno_column.set_resizable(False)
 
360
        self.revno_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
361
        self.revno_column.set_fixed_width(cell.get_size(self.treeview)[2])
 
362
        self.revno_column.pack_start(cell, expand=True)
390
363
        self.revno_column.add_attribute(cell, "text", treemodel.REVNO)
391
364
        self.treeview.append_column(self.revno_column)
392
365
 
393
366
        self.graph_cell = CellRendererGraph()
394
 
        self.graph_column = Gtk.TreeViewColumn()
395
 
        self.graph_column.set_resizable(True)
396
 
        self.graph_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
397
 
        self.graph_column.pack_start(self.graph_cell, True)
398
 
        self.graph_column.add_attribute(
399
 
            self.graph_cell, "node", treemodel.NODE)
400
 
        self.graph_column.add_attribute(
401
 
            self.graph_cell, "tags", treemodel.TAGS)
402
 
        self.graph_column.add_attribute(
403
 
            self.graph_cell, "in-lines", treemodel.LAST_LINES)
404
 
        self.graph_column.add_attribute(
405
 
            self.graph_cell, "out-lines", treemodel.LINES)
 
367
        self.graph_column = gtk.TreeViewColumn()
 
368
        self.graph_column.set_resizable(False)
 
369
        self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
370
        self.graph_column.pack_start(self.graph_cell, expand=True)
 
371
        self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
 
372
        self.graph_column.add_attribute(self.graph_cell, "tags", treemodel.TAGS)
 
373
        self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
 
374
        self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
406
375
        self.treeview.append_column(self.graph_column)
407
376
 
408
 
        cell = Gtk.CellRendererText()
 
377
        cell = gtk.CellRendererText()
409
378
        cell.set_property("width-chars", 65)
410
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
411
 
        self.summary_column = Gtk.TreeViewColumn("Summary")
412
 
        self.summary_column.set_resizable(True)
 
379
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
380
        self.summary_column = gtk.TreeViewColumn("Summary")
 
381
        self.summary_column.set_resizable(False)
413
382
        self.summary_column.set_expand(True)
414
 
        self.summary_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
415
 
        self.summary_column.set_fixed_width(
416
 
            cell.get_preferred_width(self.treeview)[1])
417
 
        self.summary_column.pack_start(cell, True)
 
383
        self.summary_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
384
        self.summary_column.set_fixed_width(cell.get_size(self.treeview)[2])
 
385
        self.summary_column.pack_start(cell, expand=True)
418
386
        self.summary_column.add_attribute(cell, "markup", treemodel.SUMMARY)
419
387
        self.treeview.append_column(self.summary_column)
420
388
 
421
 
        cell = Gtk.CellRendererText()
 
389
        cell = gtk.CellRendererText()
422
390
        cell.set_property("width-chars", 15)
423
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
424
 
        self.authors_column = Gtk.TreeViewColumn("Author(s)")
425
 
        self.authors_column.set_resizable(False)
426
 
        self.authors_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
427
 
        self.authors_column.set_fixed_width(200)
428
 
        self.authors_column.pack_start(cell, True)
429
 
        self.authors_column.add_attribute(cell, "text", treemodel.AUTHORS)
430
 
        self.treeview.append_column(self.authors_column)
 
391
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
392
        self.committer_column = gtk.TreeViewColumn("Committer")
 
393
        self.committer_column.set_resizable(False)
 
394
        self.committer_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
395
        self.committer_column.set_fixed_width(200)
 
396
        self.committer_column.pack_start(cell, expand=True)
 
397
        self.committer_column.add_attribute(cell, "text", treemodel.COMMITTER)
 
398
        self.treeview.append_column(self.committer_column)
431
399
 
432
 
        cell = Gtk.CellRendererText()
 
400
        cell = gtk.CellRendererText()
433
401
        cell.set_property("width-chars", 20)
434
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
435
 
        self.date_column = Gtk.TreeViewColumn("Date")
 
402
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
403
        self.date_column = gtk.TreeViewColumn("Date")
436
404
        self.date_column.set_visible(False)
437
 
        self.date_column.set_resizable(True)
438
 
        self.date_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
 
405
        self.date_column.set_resizable(False)
 
406
        self.date_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
439
407
        self.date_column.set_fixed_width(130)
440
 
        self.date_column.pack_start(cell, True)
 
408
        self.date_column.pack_start(cell, expand=True)
441
409
        self.date_column.add_attribute(cell, "text", treemodel.TIMESTAMP)
442
410
        self.treeview.append_column(self.date_column)
443
 
 
 
411
        
444
412
        return self.treeview
445
 
 
 
413
    
446
414
    def _on_selection_changed(self, treeview):
447
415
        """callback for when the treeview changes."""
448
416
        (path, focus) = treeview.get_cursor()
454
422
    def _on_revision_selected(self, widget, event):
455
423
        from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
456
424
        if event.button == 3:
457
 
            revs = []
458
 
            rev = self.get_revision()
459
 
            if rev is not None:
460
 
                revs.append(rev.revision_id)
461
 
            menu = RevisionMenu(self.branch.repository, revs, self.branch)
 
425
            menu = RevisionMenu(self.branch.repository, 
 
426
                [self.get_revision().revision_id],
 
427
                self.branch)
462
428
            menu.connect('tag-added', lambda w, t, r: self.add_tag(t, r))
463
 
            menu.popup(None, None, None, None, event.button, event.get_time())
 
429
            menu.popup(None, None, None, event.button, event.get_time())
464
430
 
465
431
    def _on_revision_activated(self, widget, path, col):
466
432
        self.emit('revision-activated', path, col)