/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: 2011-02-18 11:53:18 UTC
  • mfrom: (452.5.2 viz-locks)
  • Revision ID: jelmer@samba.org-20110218115318-xgsxhn13fd3m6nj9
Add lock dialog. (Daniel Schierbeck)

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
 
 
16
24
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
 
 
27
class TreeView(gtk.VBox):
24
28
 
25
29
    __gproperties__ = {
26
 
        'branch': (GObject.TYPE_PYOBJECT,
 
30
        'branch': (gobject.TYPE_PYOBJECT,
27
31
                   'Branch',
28
32
                   'The Bazaar branch being visualized',
29
 
                   GObject.PARAM_CONSTRUCT_ONLY | GObject.PARAM_WRITABLE),
 
33
                   gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE),
30
34
 
31
 
        'revision': (GObject.TYPE_PYOBJECT,
 
35
        'revision': (gobject.TYPE_PYOBJECT,
32
36
                     'Revision',
33
37
                     'The currently selected revision',
34
 
                     GObject.PARAM_READWRITE),
 
38
                     gobject.PARAM_READWRITE),
35
39
 
36
 
        'revision-number': (GObject.TYPE_STRING,
 
40
        'revision-number': (gobject.TYPE_STRING,
37
41
                            'Revision number',
38
42
                            'The number of the selected revision',
39
43
                            '',
40
 
                            GObject.PARAM_READABLE),
 
44
                            gobject.PARAM_READABLE),
41
45
 
42
 
        'children': (GObject.TYPE_PYOBJECT,
 
46
        'children': (gobject.TYPE_PYOBJECT,
43
47
                     'Child revisions',
44
48
                     'Children of the currently selected revision',
45
 
                     GObject.PARAM_READABLE),
 
49
                     gobject.PARAM_READABLE),
46
50
 
47
 
        'parents': (GObject.TYPE_PYOBJECT,
 
51
        'parents': (gobject.TYPE_PYOBJECT,
48
52
                    'Parent revisions',
49
53
                    'Parents to the currently selected revision',
50
 
                    GObject.PARAM_READABLE),
 
54
                    gobject.PARAM_READABLE),
51
55
 
52
 
        'revno-column-visible': (GObject.TYPE_BOOLEAN,
 
56
        'revno-column-visible': (gobject.TYPE_BOOLEAN,
53
57
                                 'Revision number column',
54
58
                                 'Show revision number column',
55
59
                                 True,
56
 
                                 GObject.PARAM_READWRITE),
 
60
                                 gobject.PARAM_READWRITE),
57
61
 
58
 
        'graph-column-visible': (GObject.TYPE_BOOLEAN,
 
62
        'graph-column-visible': (gobject.TYPE_BOOLEAN,
59
63
                                 'Graph column',
60
64
                                 'Show graph column',
61
65
                                 True,
62
 
                                 GObject.PARAM_READWRITE),
 
66
                                 gobject.PARAM_READWRITE),
63
67
 
64
 
        'date-column-visible': (GObject.TYPE_BOOLEAN,
 
68
        'date-column-visible': (gobject.TYPE_BOOLEAN,
65
69
                                 'Date',
66
70
                                 'Show date column',
67
71
                                 False,
68
 
                                 GObject.PARAM_READWRITE),
 
72
                                 gobject.PARAM_READWRITE),
69
73
 
70
 
        'compact': (GObject.TYPE_BOOLEAN,
 
74
        'compact': (gobject.TYPE_BOOLEAN,
71
75
                    'Compact view',
72
76
                    'Break ancestry lines to save space',
73
77
                    True,
74
 
                    GObject.PARAM_CONSTRUCT | GObject.PARAM_READWRITE),
 
78
                    gobject.PARAM_CONSTRUCT | gobject.PARAM_READWRITE),
75
79
 
76
 
        'mainline-only': (GObject.TYPE_BOOLEAN,
 
80
        'mainline-only': (gobject.TYPE_BOOLEAN,
77
81
                    'Mainline only',
78
82
                    'Only show the mainline history.',
79
83
                    False,
80
 
                    GObject.PARAM_CONSTRUCT | GObject.PARAM_READWRITE),
 
84
                    gobject.PARAM_CONSTRUCT | gobject.PARAM_READWRITE),
81
85
 
82
86
    }
83
87
 
84
88
    __gsignals__ = {
85
 
        'revision-selected': (GObject.SignalFlags.RUN_FIRST,
86
 
                              None,
 
89
        'revision-selected': (gobject.SIGNAL_RUN_FIRST,
 
90
                              gobject.TYPE_NONE,
87
91
                              ()),
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,
 
92
        'revision-activated': (gobject.SIGNAL_RUN_FIRST,
 
93
                              gobject.TYPE_NONE,
 
94
                              (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
 
95
        'tag-added': (gobject.SIGNAL_RUN_FIRST,
 
96
                              gobject.TYPE_NONE,
 
97
                              (gobject.TYPE_STRING, gobject.TYPE_STRING)),
 
98
        'refreshed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
95
99
                              ())
96
100
    }
97
101
 
105
109
        :param broken_line_length: After how much lines to break 
106
110
                                   branches.
107
111
        """
108
 
        super(TreeView, self).__init__(homogeneous=False, spacing=0)
 
112
        gtk.VBox.__init__(self, spacing=0)
109
113
 
110
114
        self.progress_widget = ProgressPanel()
111
 
        self.pack_start(self.progress_widget, False, True, 0)
 
115
        self.pack_start(self.progress_widget, expand=False, fill=True)
112
116
        if getattr(ui.ui_factory, "set_progress_bar_widget", None) is not None:
113
117
            # We'are using our own ui, let's tell it to use our widget.
114
118
            ui.ui_factory.set_progress_bar_widget(self.progress_widget)
115
119
 
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)
 
120
        self.scrolled_window = gtk.ScrolledWindow()
 
121
        self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
 
122
                                        gtk.POLICY_AUTOMATIC)
 
123
        self.scrolled_window.set_shadow_type(gtk.SHADOW_IN)
120
124
        self.scrolled_window.show()
121
 
        self.pack_start(self.scrolled_window, True, True, 0)
 
125
        self.pack_start(self.scrolled_window, expand=True, fill=True)
122
126
 
123
127
        self.scrolled_window.add(self.construct_treeview())
124
128
 
131
135
        self.maxnum = maxnum
132
136
        self.compact = compact
133
137
 
134
 
        self.model = treemodel.BranchTreeModel(self.branch, [])
135
 
        GObject.idle_add(self.populate)
 
138
        gobject.idle_add(self.populate)
136
139
 
137
140
        self.connect("destroy", self._on_destroy)
138
141
 
156
159
        elif property.name == 'branch':
157
160
            return self.branch
158
161
        elif property.name == 'revision':
159
 
            if self.path is None:
160
 
                return None
161
162
            return self.model.get_value(self.model.get_iter(self.path),
162
163
                                        treemodel.REVISION)
163
164
        elif property.name == 'revision-number':
164
 
            if self.path is None:
165
 
                return None
166
165
            return self.model.get_value(self.model.get_iter(self.path),
167
166
                                        treemodel.REVNO)
168
167
        elif property.name == 'children':
169
 
            if self.path is None:
170
 
                return None
171
168
            return self.model.get_value(self.model.get_iter(self.path),
172
169
                                        treemodel.CHILDREN)
173
170
        elif property.name == 'parents':
174
 
            if self.path is None:
175
 
                return None
176
171
            return self.model.get_value(self.model.get_iter(self.path),
177
172
                                        treemodel.PARENTS)
178
173
        else:
211
206
 
212
207
        :param revid: Revision id of revision to display.
213
208
        """
214
 
        self.treeview.set_cursor(
215
 
            Gtk.TreePath(path=self.index[revid]), None, False)
 
209
        self.treeview.set_cursor(self.index[revid])
216
210
        self.treeview.grab_focus()
217
211
 
218
212
    def get_children(self):
242
236
            lock.acquire(self.branch, lock.READ)
243
237
 
244
238
            self.emit('tag-added', tag, revid)
245
 
 
 
239
        
246
240
    def refresh(self):
247
 
        GObject.idle_add(self.populate, self.get_revision())
 
241
        gobject.idle_add(self.populate, self.get_revision())
248
242
 
249
243
    def update(self):
250
244
        try:
260
254
    def back(self):
261
255
        """Signal handler for the Back button."""
262
256
        parents = self.get_parents()
263
 
        if not parents:
 
257
        if not len(parents):
264
258
            return
265
259
 
266
260
        for parent_id in parents:
275
269
    def forward(self):
276
270
        """Signal handler for the Forward button."""
277
271
        children = self.get_children()
278
 
        if not children:
 
272
        if not len(children):
279
273
            return
280
274
 
281
275
        for child_id in children:
308
302
                broken_line_length = 32
309
303
            else:
310
304
                broken_line_length = None
311
 
 
 
305
            
312
306
            show_graph = self.graph_column.get_visible()
313
307
 
314
308
            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)
 
309
            (linegraphdata, index, columns_len) = linegraph(self.branch.repository.get_graph(),
 
310
                                                            self.start,
 
311
                                                            self.maxnum, 
 
312
                                                            broken_line_length,
 
313
                                                            show_graph,
 
314
                                                            self.mainline_only,
 
315
                                                            self.progress_bar)
323
316
 
324
 
            self.model.set_line_graph_data(linegraphdata)
 
317
            self.model = TreeModel(self.branch, linegraphdata)
325
318
            self.graph_cell.columns_len = columns_len
326
 
            width = self.graph_cell.get_preferred_width(self.treeview)[1]
 
319
            width = self.graph_cell.get_size(self.treeview)[2]
327
320
            if width > 500:
328
321
                width = 500
329
 
            elif width == 0:
330
 
                # The get_preferred_width() call got an insane value.
331
 
                width = 200
332
322
            self.graph_column.set_fixed_width(width)
333
323
            self.graph_column.set_max_width(width)
334
324
            self.index = index
335
325
            self.treeview.set_model(self.model)
336
326
 
337
327
            if not revision or revision == NULL_REVISION:
338
 
                self.treeview.set_cursor(Gtk.TreePath(path=0), None, False)
 
328
                self.treeview.set_cursor(0)
339
329
            else:
340
330
                self.set_revision(revision)
341
331
 
345
335
            self.progress_bar.finished()
346
336
 
347
337
    def construct_treeview(self):
348
 
        self.treeview = Gtk.TreeView()
 
338
        self.treeview = gtk.TreeView()
349
339
 
350
340
        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)
 
341
        self.treeview.set_search_column(treemodel.REVNO)
 
342
        
 
343
        # Fix old PyGTK bug - by JAM
 
344
        set_tooltip = getattr(self.treeview, 'set_tooltip_column', None)
 
345
        if set_tooltip is not None:
 
346
            set_tooltip(treemodel.MESSAGE)
366
347
 
367
348
        self._prev_cursor_path = None
368
349
        self.treeview.connect("cursor-changed",
378
359
 
379
360
        self.treeview.show()
380
361
 
381
 
        cell = Gtk.CellRendererText()
 
362
        cell = gtk.CellRendererText()
382
363
        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)
 
364
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
365
        self.revno_column = gtk.TreeViewColumn("Revision No")
 
366
        self.revno_column.set_resizable(False)
 
367
        self.revno_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
368
        self.revno_column.set_fixed_width(cell.get_size(self.treeview)[2])
 
369
        self.revno_column.pack_start(cell, expand=True)
390
370
        self.revno_column.add_attribute(cell, "text", treemodel.REVNO)
391
371
        self.treeview.append_column(self.revno_column)
392
372
 
393
373
        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)
 
374
        self.graph_column = gtk.TreeViewColumn()
 
375
        self.graph_column.set_resizable(False)
 
376
        self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
377
        self.graph_column.pack_start(self.graph_cell, expand=True)
 
378
        self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
 
379
        self.graph_column.add_attribute(self.graph_cell, "tags", treemodel.TAGS)
 
380
        self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
 
381
        self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
406
382
        self.treeview.append_column(self.graph_column)
407
383
 
408
 
        cell = Gtk.CellRendererText()
 
384
        cell = gtk.CellRendererText()
409
385
        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)
 
386
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
387
        self.summary_column = gtk.TreeViewColumn("Summary")
 
388
        self.summary_column.set_resizable(False)
413
389
        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)
 
390
        self.summary_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
391
        self.summary_column.set_fixed_width(cell.get_size(self.treeview)[2])
 
392
        self.summary_column.pack_start(cell, expand=True)
418
393
        self.summary_column.add_attribute(cell, "markup", treemodel.SUMMARY)
419
394
        self.treeview.append_column(self.summary_column)
420
395
 
421
 
        cell = Gtk.CellRendererText()
 
396
        cell = gtk.CellRendererText()
422
397
        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)
 
398
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
399
        self.committer_column = gtk.TreeViewColumn("Committer")
 
400
        self.committer_column.set_resizable(False)
 
401
        self.committer_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
402
        self.committer_column.set_fixed_width(200)
 
403
        self.committer_column.pack_start(cell, expand=True)
 
404
        self.committer_column.add_attribute(cell, "text", treemodel.COMMITTER)
 
405
        self.treeview.append_column(self.committer_column)
431
406
 
432
 
        cell = Gtk.CellRendererText()
 
407
        cell = gtk.CellRendererText()
433
408
        cell.set_property("width-chars", 20)
434
 
        cell.set_property("ellipsize", Pango.EllipsizeMode.END)
435
 
        self.date_column = Gtk.TreeViewColumn("Date")
 
409
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
410
        self.date_column = gtk.TreeViewColumn("Date")
436
411
        self.date_column.set_visible(False)
437
 
        self.date_column.set_resizable(True)
438
 
        self.date_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
 
412
        self.date_column.set_resizable(False)
 
413
        self.date_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
439
414
        self.date_column.set_fixed_width(130)
440
 
        self.date_column.pack_start(cell, True)
 
415
        self.date_column.pack_start(cell, expand=True)
441
416
        self.date_column.add_attribute(cell, "text", treemodel.TIMESTAMP)
442
417
        self.treeview.append_column(self.date_column)
443
 
 
 
418
        
444
419
        return self.treeview
445
 
 
 
420
    
446
421
    def _on_selection_changed(self, treeview):
447
422
        """callback for when the treeview changes."""
448
423
        (path, focus) = treeview.get_cursor()
454
429
    def _on_revision_selected(self, widget, event):
455
430
        from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
456
431
        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)
 
432
            menu = RevisionMenu(self.branch.repository, 
 
433
                [self.get_revision().revision_id],
 
434
                self.branch)
462
435
            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())
 
436
            menu.popup(None, None, None, event.button, event.get_time())
464
437
 
465
438
    def _on_revision_activated(self, widget, path, col):
466
439
        self.emit('revision-activated', path, col)