6
6
__copyright__ = "Copyright © 2005 Canonical Ltd."
7
7
__author__ = "Daniel Schierbeck <daniel.schierbeck@gmail.com>"
9
from gi.repository import Gtk
10
from gi.repository import GObject
11
from gi.repository import Pango
13
13
from bzrlib import ui
15
from bzrlib.plugins.gtk.ui import ProgressPanel
16
from linegraph import linegraph, same_branch
17
from graphcell import CellRendererGraph
18
from treemodel import TreeModel
14
19
from bzrlib.revision import NULL_REVISION
16
20
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
23
class TreeView(Gtk.VBox):
23
class TreeView(gtk.VBox):
25
25
__gproperties__ = {
26
'branch': (GObject.TYPE_PYOBJECT,
26
'branch': (gobject.TYPE_PYOBJECT,
28
28
'The Bazaar branch being visualized',
29
GObject.PARAM_CONSTRUCT_ONLY | GObject.PARAM_WRITABLE),
29
gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE),
31
'revision': (GObject.TYPE_PYOBJECT,
31
'revision': (gobject.TYPE_PYOBJECT,
33
33
'The currently selected revision',
34
GObject.PARAM_READWRITE),
34
gobject.PARAM_READWRITE),
36
'revision-number': (GObject.TYPE_STRING,
36
'revision-number': (gobject.TYPE_STRING,
38
38
'The number of the selected revision',
40
GObject.PARAM_READABLE),
40
gobject.PARAM_READABLE),
42
'children': (GObject.TYPE_PYOBJECT,
42
'children': (gobject.TYPE_PYOBJECT,
44
44
'Children of the currently selected revision',
45
GObject.PARAM_READABLE),
45
gobject.PARAM_READABLE),
47
'parents': (GObject.TYPE_PYOBJECT,
47
'parents': (gobject.TYPE_PYOBJECT,
48
48
'Parent revisions',
49
49
'Parents to the currently selected revision',
50
GObject.PARAM_READABLE),
50
gobject.PARAM_READABLE),
52
'revno-column-visible': (GObject.TYPE_BOOLEAN,
52
'revno-column-visible': (gobject.TYPE_BOOLEAN,
53
53
'Revision number column',
54
54
'Show revision number column',
56
GObject.PARAM_READWRITE),
56
gobject.PARAM_READWRITE),
58
'graph-column-visible': (GObject.TYPE_BOOLEAN,
58
'graph-column-visible': (gobject.TYPE_BOOLEAN,
60
60
'Show graph column',
62
GObject.PARAM_READWRITE),
62
gobject.PARAM_READWRITE),
64
'date-column-visible': (GObject.TYPE_BOOLEAN,
64
'date-column-visible': (gobject.TYPE_BOOLEAN,
66
66
'Show date column',
68
GObject.PARAM_READWRITE),
68
gobject.PARAM_READWRITE),
70
'compact': (GObject.TYPE_BOOLEAN,
70
'compact': (gobject.TYPE_BOOLEAN,
72
72
'Break ancestry lines to save space',
74
GObject.PARAM_CONSTRUCT | GObject.PARAM_READWRITE),
74
gobject.PARAM_CONSTRUCT | gobject.PARAM_READWRITE),
76
'mainline-only': (GObject.TYPE_BOOLEAN,
76
'mainline-only': (gobject.TYPE_BOOLEAN,
78
78
'Only show the mainline history.',
80
GObject.PARAM_CONSTRUCT | GObject.PARAM_READWRITE),
80
gobject.PARAM_CONSTRUCT | gobject.PARAM_READWRITE),
85
'revision-selected': (GObject.SignalFlags.RUN_FIRST,
85
'revision-selected': (gobject.SIGNAL_RUN_FIRST,
88
'revision-activated': (GObject.SignalFlags.RUN_FIRST,
90
(GObject.TYPE_PYOBJECT, GObject.TYPE_PYOBJECT)),
91
'tag-added': (GObject.SignalFlags.RUN_FIRST,
93
(GObject.TYPE_STRING, GObject.TYPE_STRING)),
94
'refreshed': (GObject.SignalFlags.RUN_FIRST, None,
88
'revision-activated': (gobject.SIGNAL_RUN_FIRST,
90
(gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)),
91
'tag-added': (gobject.SIGNAL_RUN_FIRST,
93
(gobject.TYPE_STRING, gobject.TYPE_STRING)),
94
'refreshed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
105
105
:param broken_line_length: After how much lines to break
108
super(TreeView, self).__init__(homogeneous=False, spacing=0)
108
gtk.VBox.__init__(self, spacing=0)
110
110
self.progress_widget = ProgressPanel()
111
self.pack_start(self.progress_widget, False, True, 0)
111
self.pack_start(self.progress_widget, expand=False, fill=True)
112
112
if getattr(ui.ui_factory, "set_progress_bar_widget", None) is not None:
113
113
# We'are using our own ui, let's tell it to use our widget.
114
114
ui.ui_factory.set_progress_bar_widget(self.progress_widget)
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)
123
123
self.scrolled_window.add(self.construct_treeview())
308
298
broken_line_length = 32
310
300
broken_line_length = None
312
302
show_graph = self.graph_column.get_visible()
314
304
self.branch.lock_read()
315
(linegraphdata, index, columns_len) = linegraph(
316
self.branch.repository.get_graph(),
305
(linegraphdata, index, columns_len) = linegraph(self.branch.repository.get_graph(),
324
self.model.set_line_graph_data(linegraphdata)
313
self.model = TreeModel(self.branch, linegraphdata)
325
314
self.graph_cell.columns_len = columns_len
326
width = self.graph_cell.get_preferred_width(self.treeview)[1]
315
width = self.graph_cell.get_size(self.treeview)[2]
330
# The get_preferred_width() call got an insane value.
332
318
self.graph_column.set_fixed_width(width)
333
319
self.graph_column.set_max_width(width)
334
320
self.index = index
335
321
self.treeview.set_model(self.model)
337
323
if not revision or revision == NULL_REVISION:
338
self.treeview.set_cursor(Gtk.TreePath(path=0), None, False)
324
self.treeview.set_cursor(0)
340
326
self.set_revision(revision)
345
331
self.progress_bar.finished()
347
333
def construct_treeview(self):
348
self.treeview = Gtk.TreeView()
334
self.treeview = gtk.TreeView()
350
336
self.treeview.set_rules_hint(True)
351
# combined revno/summary interactive search
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)
361
self.treeview.set_search_equal_func(search_equal_func, None)
362
self.treeview.set_enable_search(True)
364
self.treeview.set_tooltip_column(treemodel.MESSAGE)
365
self.treeview.set_headers_visible(True)
337
self.treeview.set_search_column(treemodel.REVNO)
339
# Fix old PyGTK bug - by JAM
340
set_tooltip = getattr(self.treeview, 'set_tooltip_column', None)
341
if set_tooltip is not None:
342
set_tooltip(treemodel.MESSAGE)
367
344
self._prev_cursor_path = None
368
345
self.treeview.connect("cursor-changed",
379
356
self.treeview.show()
381
cell = Gtk.CellRendererText()
358
cell = gtk.CellRendererText()
382
359
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)
360
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
361
self.revno_column = gtk.TreeViewColumn("Revision No")
362
self.revno_column.set_resizable(False)
363
self.revno_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
364
self.revno_column.set_fixed_width(cell.get_size(self.treeview)[2])
365
self.revno_column.pack_start(cell, expand=True)
390
366
self.revno_column.add_attribute(cell, "text", treemodel.REVNO)
391
367
self.treeview.append_column(self.revno_column)
393
369
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)
370
self.graph_column = gtk.TreeViewColumn()
371
self.graph_column.set_resizable(False)
372
self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
373
self.graph_column.pack_start(self.graph_cell, expand=True)
374
self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
375
self.graph_column.add_attribute(self.graph_cell, "tags", treemodel.TAGS)
376
self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
377
self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
406
378
self.treeview.append_column(self.graph_column)
408
cell = Gtk.CellRendererText()
380
cell = gtk.CellRendererText()
409
381
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)
382
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
383
self.summary_column = gtk.TreeViewColumn("Summary")
384
self.summary_column.set_resizable(False)
413
385
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)
386
self.summary_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
387
self.summary_column.set_fixed_width(cell.get_size(self.treeview)[2])
388
self.summary_column.pack_start(cell, expand=True)
418
389
self.summary_column.add_attribute(cell, "markup", treemodel.SUMMARY)
419
390
self.treeview.append_column(self.summary_column)
421
cell = Gtk.CellRendererText()
392
cell = gtk.CellRendererText()
422
393
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)
394
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
395
self.committer_column = gtk.TreeViewColumn("Committer")
396
self.committer_column.set_resizable(False)
397
self.committer_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
398
self.committer_column.set_fixed_width(200)
399
self.committer_column.pack_start(cell, expand=True)
400
self.committer_column.add_attribute(cell, "text", treemodel.COMMITTER)
401
self.treeview.append_column(self.committer_column)
432
cell = Gtk.CellRendererText()
403
cell = gtk.CellRendererText()
433
404
cell.set_property("width-chars", 20)
434
cell.set_property("ellipsize", Pango.EllipsizeMode.END)
435
self.date_column = Gtk.TreeViewColumn("Date")
405
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
406
self.date_column = gtk.TreeViewColumn("Date")
436
407
self.date_column.set_visible(False)
437
self.date_column.set_resizable(True)
438
self.date_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
408
self.date_column.set_resizable(False)
409
self.date_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
439
410
self.date_column.set_fixed_width(130)
440
self.date_column.pack_start(cell, True)
411
self.date_column.pack_start(cell, expand=True)
441
412
self.date_column.add_attribute(cell, "text", treemodel.TIMESTAMP)
442
413
self.treeview.append_column(self.date_column)
444
415
return self.treeview
446
417
def _on_selection_changed(self, treeview):
447
418
"""callback for when the treeview changes."""
448
419
(path, focus) = treeview.get_cursor()
454
425
def _on_revision_selected(self, widget, event):
455
426
from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
456
427
if event.button == 3:
458
rev = self.get_revision()
460
revs.append(rev.revision_id)
461
menu = RevisionMenu(self.branch.repository, revs, self.branch)
428
menu = RevisionMenu(self.branch.repository,
429
[self.get_revision().revision_id],
462
431
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())
432
menu.popup(None, None, None, event.button, event.get_time())
465
434
def _on_revision_activated(self, widget, path, col):
466
435
self.emit('revision-activated', path, col)