/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: Jelmer Vernooij
  • Date: 2008-06-29 18:12:29 UTC
  • mto: This revision was merged to the branch mainline in revision 519.
  • Revision ID: jelmer@samba.org-20080629181229-1l2m4cf7vvbyh8qg
Simplify progress bar code, use embedded progress bar inside viz window.

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,
95
 
                              ())
 
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))
96
97
    }
97
98
 
98
99
    def __init__(self, branch, start, maxnum, compact=True):
105
106
        :param broken_line_length: After how much lines to break 
106
107
                                   branches.
107
108
        """
108
 
        GObject.GObject.__init__(self, spacing=0)
109
 
 
110
 
        self.progress_widget = ProgressPanel()
111
 
        self.pack_start(self.progress_widget, expand=False, fill=True)
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)
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)
 
109
        gtk.VBox.__init__(self, spacing=0)
 
110
 
 
111
        loading_msg_widget = ProgressPanel()
 
112
        ui.ui_factory.set_nested_progress_bar_widget(loading_msg_widget.get_progress_bar)
 
113
        self.pack_start(loading_msg_widget, expand=False, fill=True)
 
114
 
 
115
        self.scrolled_window = gtk.ScrolledWindow()
 
116
        self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
 
117
                                        gtk.POLICY_AUTOMATIC)
 
118
        self.scrolled_window.set_shadow_type(gtk.SHADOW_IN)
120
119
        self.scrolled_window.show()
121
120
        self.pack_start(self.scrolled_window, expand=True, fill=True)
122
121
 
123
122
        self.scrolled_window.add(self.construct_treeview())
124
123
 
125
 
        self.path = None
 
124
        self.iter = None
126
125
        self.branch = branch
127
126
        self.revision = None
128
 
        self.index = {}
129
127
 
130
128
        self.start = start
131
129
        self.maxnum = maxnum
132
130
        self.compact = compact
133
131
 
134
 
        self.model = treemodel.TreeModel(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)
 
132
        gobject.idle_add(self.populate)
 
133
 
 
134
        self.connect("destroy", lambda x: self.branch.unlock())
144
135
 
145
136
    def do_get_property(self, property):
146
137
        if property.name == 'revno-column-visible':
156
147
        elif property.name == 'branch':
157
148
            return self.branch
158
149
        elif property.name == 'revision':
159
 
            if self.path is None:
160
 
                return None
161
 
            return self.model.get_value(self.model.get_iter(self.path),
162
 
                                        treemodel.REVISION)
 
150
            return self.model.get_value(self.iter, treemodel.REVISION)
163
151
        elif property.name == 'revision-number':
164
 
            if self.path is None:
165
 
                return None
166
 
            return self.model.get_value(self.model.get_iter(self.path),
167
 
                                        treemodel.REVNO)
 
152
            return self.model.get_value(self.iter, treemodel.REVNO)
168
153
        elif property.name == 'children':
169
 
            if self.path is None:
170
 
                return None
171
 
            return self.model.get_value(self.model.get_iter(self.path),
172
 
                                        treemodel.CHILDREN)
 
154
            return self.model.get_value(self.iter, treemodel.CHILDREN)
173
155
        elif property.name == 'parents':
174
 
            if self.path is None:
175
 
                return None
176
 
            return self.model.get_value(self.model.get_iter(self.path),
177
 
                                        treemodel.PARENTS)
 
156
            return self.model.get_value(self.iter, treemodel.PARENTS)
178
157
        else:
179
158
            raise AttributeError, 'unknown property %s' % property.name
180
159
 
200
179
        """Return revision id of currently selected revision, or None."""
201
180
        return self.get_property('revision')
202
181
 
203
 
    def has_revision_id(self, revision_id):
204
 
        return (revision_id in self.index)
205
 
 
206
182
    def set_revision(self, revision):
207
183
        self.set_property('revision', revision)
208
184
 
231
207
    def add_tag(self, tag, revid=None):
232
208
        if revid is None: revid = self.revision.revision_id
233
209
 
234
 
        if lock.release(self.branch):
 
210
        try:
 
211
            self.branch.unlock()
 
212
 
235
213
            try:
236
 
                lock.acquire(self.branch, lock.WRITE)
 
214
                self.branch.lock_write()
237
215
                self.model.add_tag(tag, revid)
238
216
            finally:
239
 
                lock.release(self.branch)
240
 
 
241
 
            lock.acquire(self.branch, lock.READ)
242
 
 
243
 
            self.emit('tag-added', tag, revid)
244
 
 
 
217
                self.branch.unlock()
 
218
 
 
219
        finally:
 
220
            self.branch.lock_read()
 
221
 
 
222
        self.emit('tag-added', tag, revid)
 
223
        
245
224
    def refresh(self):
246
 
        GObject.idle_add(self.populate, self.get_revision())
 
225
        gobject.idle_add(self.populate, self.get_revision())
247
226
 
248
227
    def update(self):
249
228
        try:
296
275
                       should be broken.
297
276
        """
298
277
 
299
 
        if getattr(ui.ui_factory, "set_progress_bar_widget", None) is not None:
300
 
            # We'are using our own ui, let's tell it to use our widget.
301
 
            ui.ui_factory.set_progress_bar_widget(self.progress_widget)
302
278
        self.progress_bar = ui.ui_factory.nested_progress_bar()
303
 
        self.progress_bar.update("Loading ancestry graph", 0, 5)
 
279
        self.progress_bar.update(msg="Loading ancestry graph", total_cnt=5)
304
280
 
305
281
        try:
306
282
            if self.compact:
307
283
                broken_line_length = 32
308
284
            else:
309
285
                broken_line_length = None
310
 
 
 
286
            
311
287
            show_graph = self.graph_column.get_visible()
312
288
 
313
289
            self.branch.lock_read()
319
295
                                                            self.mainline_only,
320
296
                                                            self.progress_bar)
321
297
 
322
 
            self.model.line_graph_data = linegraphdata
 
298
            self.model = TreeModel(self.branch, linegraphdata)
323
299
            self.graph_cell.columns_len = columns_len
324
300
            width = self.graph_cell.get_size(self.treeview)[2]
325
301
            if width > 500:
334
310
            else:
335
311
                self.set_revision(revision)
336
312
 
337
 
            self.emit('refreshed')
338
313
            return False
339
314
        finally:
340
315
            self.progress_bar.finished()
341
316
 
342
317
    def construct_treeview(self):
343
 
        self.treeview = Gtk.TreeView()
 
318
        self.treeview = gtk.TreeView()
344
319
 
345
320
        self.treeview.set_rules_hint(True)
346
 
        # combined revno/summary interactive search
347
 
        #
348
 
        # the row in a treemodel is considered "matched" if a REVNO *starts*
349
 
        # from the key (that is the key is found in a REVNO at the offset 0)
350
 
        # or if a MESSAGE *contains* the key anywhere (that is, the key is
351
 
        # found case insensitively in a MESSAGE at any offset)
352
 
        def search_equal_func(model, column, key, iter):
353
 
            return (model.get_value(iter, treemodel.REVNO).find(key) != 0
354
 
                and model.get_value(iter, treemodel.MESSAGE).lower().find(key.lower()) == -1)
355
 
 
356
 
        self.treeview.set_search_equal_func(search_equal_func)
357
 
        self.treeview.set_enable_search(True)
358
 
 
359
 
        set_tooltip(treemodel.MESSAGE)
360
 
 
361
 
        self._prev_cursor_path = None
 
321
        self.treeview.set_search_column(treemodel.REVNO)
 
322
        
 
323
        # Fix old PyGTK bug - by JAM
 
324
        set_tooltip = getattr(self.treeview, 'set_tooltip_column', None)
 
325
        if set_tooltip is not None:
 
326
            set_tooltip(treemodel.MESSAGE)
 
327
 
362
328
        self.treeview.connect("cursor-changed",
363
329
                self._on_selection_changed)
364
330
 
372
338
 
373
339
        self.treeview.show()
374
340
 
375
 
        cell = Gtk.CellRendererText()
 
341
        cell = gtk.CellRendererText()
376
342
        cell.set_property("width-chars", 15)
377
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
378
 
        self.revno_column = Gtk.TreeViewColumn("Revision No")
379
 
        self.revno_column.set_resizable(True)
380
 
        self.revno_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
 
343
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
344
        self.revno_column = gtk.TreeViewColumn("Revision No")
 
345
        self.revno_column.set_resizable(False)
 
346
        self.revno_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
381
347
        self.revno_column.set_fixed_width(cell.get_size(self.treeview)[2])
382
 
        self.revno_column.pack_start(cell, True, True, 0)
 
348
        self.revno_column.pack_start(cell, expand=True)
383
349
        self.revno_column.add_attribute(cell, "text", treemodel.REVNO)
384
350
        self.treeview.append_column(self.revno_column)
385
351
 
386
352
        self.graph_cell = CellRendererGraph()
387
 
        self.graph_column = Gtk.TreeViewColumn()
388
 
        self.graph_column.set_resizable(True)
389
 
        self.graph_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
390
 
        self.graph_column.pack_start(self.graph_cell, True, True, 0)
 
353
        self.graph_column = gtk.TreeViewColumn()
 
354
        self.graph_column.set_resizable(False)
 
355
        self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
356
        self.graph_column.pack_start(self.graph_cell, expand=True)
391
357
        self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
392
358
        self.graph_column.add_attribute(self.graph_cell, "tags", treemodel.TAGS)
393
359
        self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
394
360
        self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
395
361
        self.treeview.append_column(self.graph_column)
396
362
 
397
 
        cell = Gtk.CellRendererText()
 
363
        cell = gtk.CellRendererText()
398
364
        cell.set_property("width-chars", 65)
399
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
400
 
        self.summary_column = Gtk.TreeViewColumn("Summary")
401
 
        self.summary_column.set_resizable(True)
 
365
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
366
        self.summary_column = gtk.TreeViewColumn("Summary")
 
367
        self.summary_column.set_resizable(False)
402
368
        self.summary_column.set_expand(True)
403
 
        self.summary_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
 
369
        self.summary_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
404
370
        self.summary_column.set_fixed_width(cell.get_size(self.treeview)[2])
405
 
        self.summary_column.pack_start(cell, True, True, 0)
 
371
        self.summary_column.pack_start(cell, expand=True)
406
372
        self.summary_column.add_attribute(cell, "markup", treemodel.SUMMARY)
407
373
        self.treeview.append_column(self.summary_column)
408
374
 
409
 
        cell = Gtk.CellRendererText()
 
375
        cell = gtk.CellRendererText()
410
376
        cell.set_property("width-chars", 15)
411
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
412
 
        self.authors_column = Gtk.TreeViewColumn("Author(s)")
413
 
        self.authors_column.set_resizable(False)
414
 
        self.authors_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
415
 
        self.authors_column.set_fixed_width(200)
416
 
        self.authors_column.pack_start(cell, True, True, 0)
417
 
        self.authors_column.add_attribute(cell, "text", treemodel.AUTHORS)
418
 
        self.treeview.append_column(self.authors_column)
 
377
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
378
        self.committer_column = gtk.TreeViewColumn("Committer")
 
379
        self.committer_column.set_resizable(False)
 
380
        self.committer_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
381
        self.committer_column.set_fixed_width(200)
 
382
        self.committer_column.pack_start(cell, expand=True)
 
383
        self.committer_column.add_attribute(cell, "text", treemodel.COMMITTER)
 
384
        self.treeview.append_column(self.committer_column)
419
385
 
420
 
        cell = Gtk.CellRendererText()
 
386
        cell = gtk.CellRendererText()
421
387
        cell.set_property("width-chars", 20)
422
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
423
 
        self.date_column = Gtk.TreeViewColumn("Date")
 
388
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
389
        self.date_column = gtk.TreeViewColumn("Date")
424
390
        self.date_column.set_visible(False)
425
 
        self.date_column.set_resizable(True)
426
 
        self.date_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
 
391
        self.date_column.set_resizable(False)
 
392
        self.date_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
427
393
        self.date_column.set_fixed_width(130)
428
 
        self.date_column.pack_start(cell, True, True, 0)
 
394
        self.date_column.pack_start(cell, expand=True)
429
395
        self.date_column.add_attribute(cell, "text", treemodel.TIMESTAMP)
430
396
        self.treeview.append_column(self.date_column)
431
 
 
 
397
        
432
398
        return self.treeview
433
 
 
 
399
    
434
400
    def _on_selection_changed(self, treeview):
435
401
        """callback for when the treeview changes."""
436
402
        (path, focus) = treeview.get_cursor()
437
 
        if (path is not None) and (path != self._prev_cursor_path):
438
 
            self._prev_cursor_path = path # avoid emitting twice per click
439
 
            self.path = path
 
403
        if path is not None:
 
404
            self.iter = self.model.get_iter(path)
440
405
            self.emit('revision-selected')
441
406
 
442
407
    def _on_revision_selected(self, widget, event):
443
 
        from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
 
408
        from bzrlib.plugins.gtk.revisionmenu import RevisionPopupMenu
444
409
        if event.button == 3:
445
 
            menu = RevisionMenu(self.branch.repository, 
 
410
            menu = RevisionPopupMenu(self.branch.repository, 
446
411
                [self.get_revision().revision_id],
447
412
                self.branch)
448
413
            menu.connect('tag-added', lambda w, t, r: self.add_tag(t, r))