/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: Andrew Starr-Bochicchio
  • Date: 2010-01-26 16:54:44 UTC
  • mto: This revision was merged to the branch mainline in revision 674.
  • Revision ID: a.starr.b@gmail.com-20100126165444-7rwpzdqqiodrzlba
Provide help for ginit and gtags.

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)
 
114
        self.pack_start(self.progress_widget, expand=False, fill=True)
112
115
        if getattr(ui.ui_factory, "set_progress_bar_widget", None) is not None:
113
116
            # We'are using our own ui, let's tell it to use our widget.
114
117
            ui.ui_factory.set_progress_bar_widget(self.progress_widget)
115
118
 
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)
 
119
        self.scrolled_window = gtk.ScrolledWindow()
 
120
        self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
 
121
                                        gtk.POLICY_AUTOMATIC)
 
122
        self.scrolled_window.set_shadow_type(gtk.SHADOW_IN)
120
123
        self.scrolled_window.show()
121
 
        self.pack_start(self.scrolled_window, True, True, 0)
 
124
        self.pack_start(self.scrolled_window, expand=True, fill=True)
122
125
 
123
126
        self.scrolled_window.add(self.construct_treeview())
124
127
 
131
134
        self.maxnum = maxnum
132
135
        self.compact = compact
133
136
 
134
 
        self.model = treemodel.BranchTreeModel(self.branch, [])
135
 
        GObject.idle_add(self.populate)
 
137
        gobject.idle_add(self.populate)
136
138
 
137
139
        self.connect("destroy", self._on_destroy)
138
140
 
156
158
        elif property.name == 'branch':
157
159
            return self.branch
158
160
        elif property.name == 'revision':
159
 
            if self.path is None:
160
 
                return None
161
161
            return self.model.get_value(self.model.get_iter(self.path),
162
162
                                        treemodel.REVISION)
163
163
        elif property.name == 'revision-number':
164
 
            if self.path is None:
165
 
                return None
166
164
            return self.model.get_value(self.model.get_iter(self.path),
167
165
                                        treemodel.REVNO)
168
166
        elif property.name == 'children':
169
 
            if self.path is None:
170
 
                return None
171
167
            return self.model.get_value(self.model.get_iter(self.path),
172
168
                                        treemodel.CHILDREN)
173
169
        elif property.name == 'parents':
174
 
            if self.path is None:
175
 
                return None
176
170
            return self.model.get_value(self.model.get_iter(self.path),
177
171
                                        treemodel.PARENTS)
178
172
        else:
211
205
 
212
206
        :param revid: Revision id of revision to display.
213
207
        """
214
 
        self.treeview.set_cursor(
215
 
            Gtk.TreePath(path=self.index[revid]), None, False)
 
208
        self.treeview.set_cursor(self.index[revid])
216
209
        self.treeview.grab_focus()
217
210
 
218
211
    def get_children(self):
232
225
    def add_tag(self, tag, revid=None):
233
226
        if revid is None: revid = self.revision.revision_id
234
227
 
235
 
        if lock.release(self.branch):
 
228
        try:
 
229
            self.branch.unlock()
 
230
 
236
231
            try:
237
 
                lock.acquire(self.branch, lock.WRITE)
 
232
                self.branch.lock_write()
238
233
                self.model.add_tag(tag, revid)
239
234
            finally:
240
 
                lock.release(self.branch)
241
 
 
242
 
            lock.acquire(self.branch, lock.READ)
243
 
 
244
 
            self.emit('tag-added', tag, revid)
245
 
 
 
235
                self.branch.unlock()
 
236
 
 
237
        finally:
 
238
            self.branch.lock_read()
 
239
 
 
240
        self.emit('tag-added', tag, revid)
 
241
        
246
242
    def refresh(self):
247
 
        GObject.idle_add(self.populate, self.get_revision())
 
243
        gobject.idle_add(self.populate, self.get_revision())
248
244
 
249
245
    def update(self):
250
246
        try:
260
256
    def back(self):
261
257
        """Signal handler for the Back button."""
262
258
        parents = self.get_parents()
263
 
        if not parents:
 
259
        if not len(parents):
264
260
            return
265
261
 
266
262
        for parent_id in parents:
275
271
    def forward(self):
276
272
        """Signal handler for the Forward button."""
277
273
        children = self.get_children()
278
 
        if not children:
 
274
        if not len(children):
279
275
            return
280
276
 
281
277
        for child_id in children:
308
304
                broken_line_length = 32
309
305
            else:
310
306
                broken_line_length = None
311
 
 
 
307
            
312
308
            show_graph = self.graph_column.get_visible()
313
309
 
314
310
            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)
 
311
            (linegraphdata, index, columns_len) = linegraph(self.branch.repository.get_graph(),
 
312
                                                            self.start,
 
313
                                                            self.maxnum, 
 
314
                                                            broken_line_length,
 
315
                                                            show_graph,
 
316
                                                            self.mainline_only,
 
317
                                                            self.progress_bar)
323
318
 
324
 
            self.model.set_line_graph_data(linegraphdata)
 
319
            self.model = TreeModel(self.branch, linegraphdata)
325
320
            self.graph_cell.columns_len = columns_len
326
 
            width = self.graph_cell.get_preferred_width(self.treeview)[1]
 
321
            width = self.graph_cell.get_size(self.treeview)[2]
327
322
            if width > 500:
328
323
                width = 500
329
 
            elif width == 0:
330
 
                # The get_preferred_width() call got an insane value.
331
 
                width = 200
332
324
            self.graph_column.set_fixed_width(width)
333
325
            self.graph_column.set_max_width(width)
334
326
            self.index = index
335
327
            self.treeview.set_model(self.model)
336
328
 
337
329
            if not revision or revision == NULL_REVISION:
338
 
                self.treeview.set_cursor(Gtk.TreePath(path=0), None, False)
 
330
                self.treeview.set_cursor(0)
339
331
            else:
340
332
                self.set_revision(revision)
341
333
 
345
337
            self.progress_bar.finished()
346
338
 
347
339
    def construct_treeview(self):
348
 
        self.treeview = Gtk.TreeView()
 
340
        self.treeview = gtk.TreeView()
349
341
 
350
342
        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)
 
343
        self.treeview.set_search_column(treemodel.REVNO)
 
344
        
 
345
        # Fix old PyGTK bug - by JAM
 
346
        set_tooltip = getattr(self.treeview, 'set_tooltip_column', None)
 
347
        if set_tooltip is not None:
 
348
            set_tooltip(treemodel.MESSAGE)
366
349
 
367
350
        self._prev_cursor_path = None
368
351
        self.treeview.connect("cursor-changed",
378
361
 
379
362
        self.treeview.show()
380
363
 
381
 
        cell = Gtk.CellRendererText()
 
364
        cell = gtk.CellRendererText()
382
365
        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)
 
366
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
367
        self.revno_column = gtk.TreeViewColumn("Revision No")
 
368
        self.revno_column.set_resizable(False)
 
369
        self.revno_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
370
        self.revno_column.set_fixed_width(cell.get_size(self.treeview)[2])
 
371
        self.revno_column.pack_start(cell, expand=True)
390
372
        self.revno_column.add_attribute(cell, "text", treemodel.REVNO)
391
373
        self.treeview.append_column(self.revno_column)
392
374
 
393
375
        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)
 
376
        self.graph_column = gtk.TreeViewColumn()
 
377
        self.graph_column.set_resizable(False)
 
378
        self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
379
        self.graph_column.pack_start(self.graph_cell, expand=True)
 
380
        self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
 
381
        self.graph_column.add_attribute(self.graph_cell, "tags", treemodel.TAGS)
 
382
        self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
 
383
        self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
406
384
        self.treeview.append_column(self.graph_column)
407
385
 
408
 
        cell = Gtk.CellRendererText()
 
386
        cell = gtk.CellRendererText()
409
387
        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)
 
388
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
389
        self.summary_column = gtk.TreeViewColumn("Summary")
 
390
        self.summary_column.set_resizable(False)
413
391
        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)
 
392
        self.summary_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
393
        self.summary_column.set_fixed_width(cell.get_size(self.treeview)[2])
 
394
        self.summary_column.pack_start(cell, expand=True)
418
395
        self.summary_column.add_attribute(cell, "markup", treemodel.SUMMARY)
419
396
        self.treeview.append_column(self.summary_column)
420
397
 
421
 
        cell = Gtk.CellRendererText()
 
398
        cell = gtk.CellRendererText()
422
399
        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)
 
400
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
401
        self.committer_column = gtk.TreeViewColumn("Committer")
 
402
        self.committer_column.set_resizable(False)
 
403
        self.committer_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
404
        self.committer_column.set_fixed_width(200)
 
405
        self.committer_column.pack_start(cell, expand=True)
 
406
        self.committer_column.add_attribute(cell, "text", treemodel.COMMITTER)
 
407
        self.treeview.append_column(self.committer_column)
431
408
 
432
 
        cell = Gtk.CellRendererText()
 
409
        cell = gtk.CellRendererText()
433
410
        cell.set_property("width-chars", 20)
434
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
435
 
        self.date_column = Gtk.TreeViewColumn("Date")
 
411
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
412
        self.date_column = gtk.TreeViewColumn("Date")
436
413
        self.date_column.set_visible(False)
437
 
        self.date_column.set_resizable(True)
438
 
        self.date_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
 
414
        self.date_column.set_resizable(False)
 
415
        self.date_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
439
416
        self.date_column.set_fixed_width(130)
440
 
        self.date_column.pack_start(cell, True)
 
417
        self.date_column.pack_start(cell, expand=True)
441
418
        self.date_column.add_attribute(cell, "text", treemodel.TIMESTAMP)
442
419
        self.treeview.append_column(self.date_column)
443
 
 
 
420
        
444
421
        return self.treeview
445
 
 
 
422
    
446
423
    def _on_selection_changed(self, treeview):
447
424
        """callback for when the treeview changes."""
448
425
        (path, focus) = treeview.get_cursor()
454
431
    def _on_revision_selected(self, widget, event):
455
432
        from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
456
433
        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)
 
434
            menu = RevisionMenu(self.branch.repository, 
 
435
                [self.get_revision().revision_id],
 
436
                self.branch)
462
437
            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())
 
438
            menu.popup(None, None, None, event.button, event.get_time())
464
439
 
465
440
    def _on_revision_activated(self, widget, path, col):
466
441
        self.emit('revision-activated', path, col)