/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 16:20:15 UTC
  • mto: This revision was merged to the branch mainline in revision 519.
  • Revision ID: jelmer@samba.org-20080629162015-amhe7xj4cdmup4id
Rename GtkProgressBarStack to GtkWindowProgressBarStack

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 linegraph import linegraph, same_branch
 
20
from graphcell import CellRendererGraph
 
21
from treemodel import TreeModel
14
22
from bzrlib.revision import NULL_REVISION
15
23
 
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):
 
24
class TreeView(gtk.VBox):
24
25
 
25
26
    __gproperties__ = {
26
 
        'branch': (GObject.TYPE_PYOBJECT,
 
27
        'branch': (gobject.TYPE_PYOBJECT,
27
28
                   'Branch',
28
29
                   'The Bazaar branch being visualized',
29
 
                   GObject.PARAM_CONSTRUCT_ONLY | GObject.PARAM_WRITABLE),
 
30
                   gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE),
30
31
 
31
 
        'revision': (GObject.TYPE_PYOBJECT,
 
32
        'revision': (gobject.TYPE_PYOBJECT,
32
33
                     'Revision',
33
34
                     'The currently selected revision',
34
 
                     GObject.PARAM_READWRITE),
 
35
                     gobject.PARAM_READWRITE),
35
36
 
36
 
        'revision-number': (GObject.TYPE_STRING,
 
37
        'revision-number': (gobject.TYPE_STRING,
37
38
                            'Revision number',
38
39
                            'The number of the selected revision',
39
40
                            '',
40
 
                            GObject.PARAM_READABLE),
 
41
                            gobject.PARAM_READABLE),
41
42
 
42
 
        'children': (GObject.TYPE_PYOBJECT,
 
43
        'children': (gobject.TYPE_PYOBJECT,
43
44
                     'Child revisions',
44
45
                     'Children of the currently selected revision',
45
 
                     GObject.PARAM_READABLE),
 
46
                     gobject.PARAM_READABLE),
46
47
 
47
 
        'parents': (GObject.TYPE_PYOBJECT,
 
48
        'parents': (gobject.TYPE_PYOBJECT,
48
49
                    'Parent revisions',
49
50
                    'Parents to the currently selected revision',
50
 
                    GObject.PARAM_READABLE),
 
51
                    gobject.PARAM_READABLE),
51
52
 
52
 
        'revno-column-visible': (GObject.TYPE_BOOLEAN,
 
53
        'revno-column-visible': (gobject.TYPE_BOOLEAN,
53
54
                                 'Revision number column',
54
55
                                 'Show revision number column',
55
56
                                 True,
56
 
                                 GObject.PARAM_READWRITE),
 
57
                                 gobject.PARAM_READWRITE),
57
58
 
58
 
        'graph-column-visible': (GObject.TYPE_BOOLEAN,
 
59
        'graph-column-visible': (gobject.TYPE_BOOLEAN,
59
60
                                 'Graph column',
60
61
                                 'Show graph column',
61
62
                                 True,
62
 
                                 GObject.PARAM_READWRITE),
 
63
                                 gobject.PARAM_READWRITE),
63
64
 
64
 
        'date-column-visible': (GObject.TYPE_BOOLEAN,
 
65
        'date-column-visible': (gobject.TYPE_BOOLEAN,
65
66
                                 'Date',
66
67
                                 'Show date column',
67
68
                                 False,
68
 
                                 GObject.PARAM_READWRITE),
 
69
                                 gobject.PARAM_READWRITE),
69
70
 
70
 
        'compact': (GObject.TYPE_BOOLEAN,
 
71
        'compact': (gobject.TYPE_BOOLEAN,
71
72
                    'Compact view',
72
73
                    'Break ancestry lines to save space',
73
74
                    True,
74
 
                    GObject.PARAM_CONSTRUCT | GObject.PARAM_READWRITE),
 
75
                    gobject.PARAM_CONSTRUCT | gobject.PARAM_READWRITE),
75
76
 
76
 
        'mainline-only': (GObject.TYPE_BOOLEAN,
 
77
        'mainline-only': (gobject.TYPE_BOOLEAN,
77
78
                    'Mainline only',
78
79
                    'Only show the mainline history.',
79
80
                    False,
80
 
                    GObject.PARAM_CONSTRUCT | GObject.PARAM_READWRITE),
 
81
                    gobject.PARAM_CONSTRUCT | gobject.PARAM_READWRITE),
81
82
 
82
83
    }
83
84
 
84
85
    __gsignals__ = {
85
 
        'revision-selected': (GObject.SignalFlags.RUN_FIRST,
86
 
                              None,
 
86
        'revision-selected': (gobject.SIGNAL_RUN_FIRST,
 
87
                              gobject.TYPE_NONE,
87
88
                              ()),
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
 
                              ())
 
89
        'revision-activated': (gobject.SIGNAL_RUN_FIRST,
 
90
                              gobject.TYPE_NONE,
 
91
                              (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
 
92
        'tag-added': (gobject.SIGNAL_RUN_FIRST,
 
93
                              gobject.TYPE_NONE,
 
94
                              (gobject.TYPE_STRING, gobject.TYPE_STRING))
96
95
    }
97
96
 
98
97
    def __init__(self, branch, start, maxnum, compact=True):
105
104
        :param broken_line_length: After how much lines to break 
106
105
                                   branches.
107
106
        """
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)
 
107
        gtk.VBox.__init__(self, spacing=0)
 
108
 
 
109
        self.scrolled_window = gtk.ScrolledWindow()
 
110
        self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
 
111
                                        gtk.POLICY_AUTOMATIC)
 
112
        self.scrolled_window.set_shadow_type(gtk.SHADOW_IN)
120
113
        self.scrolled_window.show()
121
114
        self.pack_start(self.scrolled_window, expand=True, fill=True)
122
115
 
123
116
        self.scrolled_window.add(self.construct_treeview())
124
117
 
125
 
        self.path = None
 
118
        self.iter = None
126
119
        self.branch = branch
127
120
        self.revision = None
128
 
        self.index = {}
129
121
 
130
122
        self.start = start
131
123
        self.maxnum = maxnum
132
124
        self.compact = compact
133
125
 
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)
 
126
        gobject.idle_add(self.populate)
 
127
 
 
128
        self.connect("destroy", lambda x: self.branch.unlock())
144
129
 
145
130
    def do_get_property(self, property):
146
131
        if property.name == 'revno-column-visible':
156
141
        elif property.name == 'branch':
157
142
            return self.branch
158
143
        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)
 
144
            return self.model.get_value(self.iter, treemodel.REVISION)
163
145
        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)
 
146
            return self.model.get_value(self.iter, treemodel.REVNO)
168
147
        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)
 
148
            return self.model.get_value(self.iter, treemodel.CHILDREN)
173
149
        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)
 
150
            return self.model.get_value(self.iter, treemodel.PARENTS)
178
151
        else:
179
152
            raise AttributeError, 'unknown property %s' % property.name
180
153
 
200
173
        """Return revision id of currently selected revision, or None."""
201
174
        return self.get_property('revision')
202
175
 
203
 
    def has_revision_id(self, revision_id):
204
 
        return (revision_id in self.index)
205
 
 
206
176
    def set_revision(self, revision):
207
177
        self.set_property('revision', revision)
208
178
 
231
201
    def add_tag(self, tag, revid=None):
232
202
        if revid is None: revid = self.revision.revision_id
233
203
 
234
 
        if lock.release(self.branch):
 
204
        try:
 
205
            self.branch.unlock()
 
206
 
235
207
            try:
236
 
                lock.acquire(self.branch, lock.WRITE)
 
208
                self.branch.lock_write()
237
209
                self.model.add_tag(tag, revid)
238
210
            finally:
239
 
                lock.release(self.branch)
240
 
 
241
 
            lock.acquire(self.branch, lock.READ)
242
 
 
243
 
            self.emit('tag-added', tag, revid)
244
 
 
 
211
                self.branch.unlock()
 
212
 
 
213
        finally:
 
214
            self.branch.lock_read()
 
215
 
 
216
        self.emit('tag-added', tag, revid)
 
217
        
245
218
    def refresh(self):
246
 
        GObject.idle_add(self.populate, self.get_revision())
 
219
        gobject.idle_add(self.populate, self.get_revision())
247
220
 
248
221
    def update(self):
249
222
        try:
296
269
                       should be broken.
297
270
        """
298
271
 
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
 
        self.progress_bar = ui.ui_factory.nested_progress_bar()
303
 
        self.progress_bar.update("Loading ancestry graph", 0, 5)
 
272
        loading_progress = ui.ui_factory.nested_progress_bar()
 
273
        loading_progress.update(msg="Loading ancestry graph", total=5)
304
274
 
305
275
        try:
306
276
            if self.compact:
307
277
                broken_line_length = 32
308
278
            else:
309
279
                broken_line_length = None
310
 
 
 
280
            
311
281
            show_graph = self.graph_column.get_visible()
312
282
 
313
283
            self.branch.lock_read()
317
287
                                                            broken_line_length,
318
288
                                                            show_graph,
319
289
                                                            self.mainline_only,
320
 
                                                            self.progress_bar)
 
290
                                                            loading_progress)
321
291
 
322
 
            self.model.line_graph_data = linegraphdata
 
292
            self.model = TreeModel(self.branch, linegraphdata)
323
293
            self.graph_cell.columns_len = columns_len
324
294
            width = self.graph_cell.get_size(self.treeview)[2]
325
295
            if width > 500:
334
304
            else:
335
305
                self.set_revision(revision)
336
306
 
337
 
            self.emit('refreshed')
 
307
            self.emit('revisions-loaded')
 
308
 
338
309
            return False
339
310
        finally:
340
 
            self.progress_bar.finished()
 
311
            loading_progress.finished()
341
312
 
342
313
    def construct_treeview(self):
343
 
        self.treeview = Gtk.TreeView()
 
314
        self.treeview = gtk.TreeView()
344
315
 
345
316
        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
 
 
 
317
        self.treeview.set_search_column(treemodel.REVNO)
 
318
        
359
319
        # Fix old PyGTK bug - by JAM
360
320
        set_tooltip = getattr(self.treeview, 'set_tooltip_column', None)
361
321
        if set_tooltip is not None:
362
322
            set_tooltip(treemodel.MESSAGE)
363
323
 
364
 
        self._prev_cursor_path = None
365
324
        self.treeview.connect("cursor-changed",
366
325
                self._on_selection_changed)
367
326
 
375
334
 
376
335
        self.treeview.show()
377
336
 
378
 
        cell = Gtk.CellRendererText()
 
337
        cell = gtk.CellRendererText()
379
338
        cell.set_property("width-chars", 15)
380
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
381
 
        self.revno_column = Gtk.TreeViewColumn("Revision No")
382
 
        self.revno_column.set_resizable(True)
383
 
        self.revno_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
 
339
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
340
        self.revno_column = gtk.TreeViewColumn("Revision No")
 
341
        self.revno_column.set_resizable(False)
 
342
        self.revno_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
384
343
        self.revno_column.set_fixed_width(cell.get_size(self.treeview)[2])
385
 
        self.revno_column.pack_start(cell, True, True, 0)
 
344
        self.revno_column.pack_start(cell, expand=True)
386
345
        self.revno_column.add_attribute(cell, "text", treemodel.REVNO)
387
346
        self.treeview.append_column(self.revno_column)
388
347
 
389
348
        self.graph_cell = CellRendererGraph()
390
 
        self.graph_column = Gtk.TreeViewColumn()
391
 
        self.graph_column.set_resizable(True)
392
 
        self.graph_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
393
 
        self.graph_column.pack_start(self.graph_cell, True, True, 0)
 
349
        self.graph_column = gtk.TreeViewColumn()
 
350
        self.graph_column.set_resizable(False)
 
351
        self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
352
        self.graph_column.pack_start(self.graph_cell, expand=True)
394
353
        self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
395
354
        self.graph_column.add_attribute(self.graph_cell, "tags", treemodel.TAGS)
396
355
        self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
397
356
        self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
398
357
        self.treeview.append_column(self.graph_column)
399
358
 
400
 
        cell = Gtk.CellRendererText()
 
359
        cell = gtk.CellRendererText()
401
360
        cell.set_property("width-chars", 65)
402
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
403
 
        self.summary_column = Gtk.TreeViewColumn("Summary")
404
 
        self.summary_column.set_resizable(True)
 
361
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
362
        self.summary_column = gtk.TreeViewColumn("Summary")
 
363
        self.summary_column.set_resizable(False)
405
364
        self.summary_column.set_expand(True)
406
 
        self.summary_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
 
365
        self.summary_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
407
366
        self.summary_column.set_fixed_width(cell.get_size(self.treeview)[2])
408
 
        self.summary_column.pack_start(cell, True, True, 0)
 
367
        self.summary_column.pack_start(cell, expand=True)
409
368
        self.summary_column.add_attribute(cell, "markup", treemodel.SUMMARY)
410
369
        self.treeview.append_column(self.summary_column)
411
370
 
412
 
        cell = Gtk.CellRendererText()
 
371
        cell = gtk.CellRendererText()
413
372
        cell.set_property("width-chars", 15)
414
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
415
 
        self.authors_column = Gtk.TreeViewColumn("Author(s)")
416
 
        self.authors_column.set_resizable(False)
417
 
        self.authors_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
418
 
        self.authors_column.set_fixed_width(200)
419
 
        self.authors_column.pack_start(cell, True, True, 0)
420
 
        self.authors_column.add_attribute(cell, "text", treemodel.AUTHORS)
421
 
        self.treeview.append_column(self.authors_column)
 
373
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
374
        self.committer_column = gtk.TreeViewColumn("Committer")
 
375
        self.committer_column.set_resizable(False)
 
376
        self.committer_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
377
        self.committer_column.set_fixed_width(200)
 
378
        self.committer_column.pack_start(cell, expand=True)
 
379
        self.committer_column.add_attribute(cell, "text", treemodel.COMMITTER)
 
380
        self.treeview.append_column(self.committer_column)
422
381
 
423
 
        cell = Gtk.CellRendererText()
 
382
        cell = gtk.CellRendererText()
424
383
        cell.set_property("width-chars", 20)
425
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
426
 
        self.date_column = Gtk.TreeViewColumn("Date")
 
384
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
385
        self.date_column = gtk.TreeViewColumn("Date")
427
386
        self.date_column.set_visible(False)
428
 
        self.date_column.set_resizable(True)
429
 
        self.date_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
 
387
        self.date_column.set_resizable(False)
 
388
        self.date_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
430
389
        self.date_column.set_fixed_width(130)
431
 
        self.date_column.pack_start(cell, True, True, 0)
 
390
        self.date_column.pack_start(cell, expand=True)
432
391
        self.date_column.add_attribute(cell, "text", treemodel.TIMESTAMP)
433
392
        self.treeview.append_column(self.date_column)
434
 
 
 
393
        
435
394
        return self.treeview
436
395
 
437
396
    def _on_selection_changed(self, treeview):
438
397
        """callback for when the treeview changes."""
439
398
        (path, focus) = treeview.get_cursor()
440
 
        if (path is not None) and (path != self._prev_cursor_path):
441
 
            self._prev_cursor_path = path # avoid emitting twice per click
442
 
            self.path = path
 
399
        if path is not None:
 
400
            self.iter = self.model.get_iter(path)
443
401
            self.emit('revision-selected')
444
402
 
445
403
    def _on_revision_selected(self, widget, event):
446
 
        from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
 
404
        from bzrlib.plugins.gtk.revisionmenu import RevisionPopupMenu
447
405
        if event.button == 3:
448
 
            menu = RevisionMenu(self.branch.repository, 
 
406
            menu = RevisionPopupMenu(self.branch.repository, 
449
407
                [self.get_revision().revision_id],
450
408
                self.branch)
451
409
            menu.connect('tag-added', lambda w, t, r: self.add_tag(t, r))