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
19
14
from bzrlib.revision import NULL_REVISION
20
16
from bzrlib.plugins.gtk import lock
23
class TreeView(gtk.VBox):
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):
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.SIGNAL_RUN_FIRST,
85
'revision-selected': (GObject.SignalFlags.RUN_FIRST,
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,
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,
105
105
:param broken_line_length: After how much lines to break
108
gtk.VBox.__init__(self, spacing=0)
108
super(TreeView, self).__init__(homogeneous=False, spacing=0)
110
110
self.progress_widget = ProgressPanel()
111
self.pack_start(self.progress_widget, expand=False, fill=True)
111
self.pack_start(self.progress_widget, False, True, 0)
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.POLICY_AUTOMATIC,
118
gtk.POLICY_AUTOMATIC)
119
self.scrolled_window.set_shadow_type(gtk.SHADOW_IN)
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
120
self.scrolled_window.show()
121
self.pack_start(self.scrolled_window, expand=True, fill=True)
121
self.pack_start(self.scrolled_window, True, True, 0)
123
123
self.scrolled_window.add(self.construct_treeview())
298
308
broken_line_length = 32
300
310
broken_line_length = None
302
312
show_graph = self.graph_column.get_visible()
304
314
self.branch.lock_read()
305
(linegraphdata, index, columns_len) = linegraph(self.branch.repository.get_graph(),
315
(linegraphdata, index, columns_len) = linegraph(
316
self.branch.repository.get_graph(),
313
self.model = TreeModel(self.branch, linegraphdata)
324
self.model.set_line_graph_data(linegraphdata)
314
325
self.graph_cell.columns_len = columns_len
315
width = self.graph_cell.get_size(self.treeview)[2]
326
width = self.graph_cell.get_preferred_width(self.treeview)[1]
330
# The get_preferred_width() call got an insane value.
318
332
self.graph_column.set_fixed_width(width)
319
333
self.graph_column.set_max_width(width)
320
334
self.index = index
321
335
self.treeview.set_model(self.model)
323
337
if not revision or revision == NULL_REVISION:
324
self.treeview.set_cursor(0)
338
self.treeview.set_cursor(Gtk.TreePath(path=0), None, False)
326
340
self.set_revision(revision)
331
345
self.progress_bar.finished()
333
347
def construct_treeview(self):
334
self.treeview = gtk.TreeView()
348
self.treeview = Gtk.TreeView()
336
350
self.treeview.set_rules_hint(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)
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)
344
367
self._prev_cursor_path = None
345
368
self.treeview.connect("cursor-changed",
356
379
self.treeview.show()
358
cell = gtk.CellRendererText()
381
cell = Gtk.CellRendererText()
359
382
cell.set_property("width-chars", 15)
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)
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
390
self.revno_column.add_attribute(cell, "text", treemodel.REVNO)
367
391
self.treeview.append_column(self.revno_column)
369
393
self.graph_cell = CellRendererGraph()
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)
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)
378
406
self.treeview.append_column(self.graph_column)
380
cell = gtk.CellRendererText()
408
cell = Gtk.CellRendererText()
381
409
cell.set_property("width-chars", 65)
382
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
383
self.summary_column = gtk.TreeViewColumn("Summary")
384
self.summary_column.set_resizable(False)
410
cell.set_property("ellipsize", Pango.EllipsizeMode.END)
411
self.summary_column = Gtk.TreeViewColumn("Summary")
412
self.summary_column.set_resizable(True)
385
413
self.summary_column.set_expand(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)
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)
389
418
self.summary_column.add_attribute(cell, "markup", treemodel.SUMMARY)
390
419
self.treeview.append_column(self.summary_column)
392
cell = gtk.CellRendererText()
421
cell = Gtk.CellRendererText()
393
422
cell.set_property("width-chars", 15)
394
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
395
self.authors_column = gtk.TreeViewColumn("Author(s)")
423
cell.set_property("ellipsize", Pango.EllipsizeMode.END)
424
self.authors_column = Gtk.TreeViewColumn("Author(s)")
396
425
self.authors_column.set_resizable(False)
397
self.authors_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
426
self.authors_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
398
427
self.authors_column.set_fixed_width(200)
399
self.authors_column.pack_start(cell, expand=True)
428
self.authors_column.pack_start(cell, True)
400
429
self.authors_column.add_attribute(cell, "text", treemodel.AUTHORS)
401
430
self.treeview.append_column(self.authors_column)
403
cell = gtk.CellRendererText()
432
cell = Gtk.CellRendererText()
404
433
cell.set_property("width-chars", 20)
405
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
406
self.date_column = gtk.TreeViewColumn("Date")
434
cell.set_property("ellipsize", Pango.EllipsizeMode.END)
435
self.date_column = Gtk.TreeViewColumn("Date")
407
436
self.date_column.set_visible(False)
408
self.date_column.set_resizable(False)
409
self.date_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
437
self.date_column.set_resizable(True)
438
self.date_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
410
439
self.date_column.set_fixed_width(130)
411
self.date_column.pack_start(cell, expand=True)
440
self.date_column.pack_start(cell, True)
412
441
self.date_column.add_attribute(cell, "text", treemodel.TIMESTAMP)
413
442
self.treeview.append_column(self.date_column)
415
444
return self.treeview
417
446
def _on_selection_changed(self, treeview):
418
447
"""callback for when the treeview changes."""
419
448
(path, focus) = treeview.get_cursor()
425
454
def _on_revision_selected(self, widget, event):
426
455
from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
427
456
if event.button == 3:
428
menu = RevisionMenu(self.branch.repository,
429
[self.get_revision().revision_id],
458
rev = self.get_revision()
460
revs.append(rev.revision_id)
461
menu = RevisionMenu(self.branch.repository, revs, self.branch)
431
462
menu.connect('tag-added', lambda w, t, r: self.add_tag(t, r))
432
menu.popup(None, None, None, event.button, event.get_time())
463
menu.popup(None, None, None, None, event.button, event.get_time())
434
465
def _on_revision_activated(self, widget, path, col):
435
466
self.emit('revision-activated', path, col)