1
# -*- coding: UTF-8 -*-
4
3
This module contains the code to manage the branch information window,
5
4
which contains both the revision graph and details panes.
8
__copyright__ = "Copyright © 2005 Canonical Ltd."
7
__copyright__ = "Copyright (c) 2005 Canonical Ltd."
9
8
__author__ = "Scott James Remnant <scott@ubuntu.com>"
16
from bzrlib.osutils import format_date
18
from graph import distances, graph, same_branch
19
from graphcell import CellRendererGraph
22
class BranchWindow(gtk.Window):
11
from gi.repository import Gdk
12
from gi.repository import Gtk
14
from bzrlib.plugins.gtk import icon_path
15
from bzrlib.plugins.gtk.branchview import TreeView
16
from bzrlib.plugins.gtk.preferences import PreferencesWindow
17
from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
18
from bzrlib.plugins.gtk.window import Window
20
from bzrlib.config import GlobalConfig
21
from bzrlib.revision import NULL_REVISION
22
from bzrlib.trace import mutter
25
class BranchWindow(Window):
25
28
This object represents and manages a single window containing information
26
29
for a particular branch.
30
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
32
def __init__(self, branch, start_revs, maxnum, parent=None):
33
"""Create a new BranchWindow.
35
:param branch: Branch object for branch to show.
36
:param start_revs: Revision ids of top revisions.
37
:param maxnum: Maximum number of revisions to display,
41
super(BranchWindow, self).__init__(parent=parent)
31
42
self.set_border_width(0)
32
self.set_title("bzrk")
34
# Use three-quarters of the screen by default
35
screen = self.get_screen()
36
monitor = screen.get_monitor_geometry(0)
37
width = int(monitor.width * 0.75)
38
height = int(monitor.height * 0.75)
45
self.start_revs = start_revs
47
self.config = GlobalConfig()
49
if self.config.get_user_option('viz-compact-view') == 'yes':
50
self.compact_view = True
52
self.compact_view = False
54
self.set_title(branch._get_nick(local=True) + " - revision history")
56
# user-configured window size
57
size = self._load_size('viz-window-size')
61
# Use three-quarters of the screen by default
62
screen = self.get_screen()
63
monitor = screen.get_monitor_geometry(0)
64
width = int(monitor.width * 0.75)
65
height = int(monitor.height * 0.75)
39
66
self.set_default_size(width, height)
67
self.set_size_request(width/3, height/3)
68
self._save_size_on_destroy(self, 'viz-window-size')
42
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
71
icon = self.render_icon_pixbuf(Gtk.STOCK_INDEX, Gtk.IconSize.BUTTON)
43
72
self.set_icon(icon)
45
self.accel_group = gtk.AccelGroup()
74
Gtk.AccelMap.add_entry("<viz>/Go/Next Revision", Gdk.KEY_Up, Gdk.ModifierType.MOD1_MASK)
75
Gtk.AccelMap.add_entry("<viz>/Go/Previous Revision", Gdk.KEY_Down, Gdk.ModifierType.MOD1_MASK)
76
Gtk.AccelMap.add_entry("<viz>/View/Refresh", Gdk.KEY_F5, 0)
78
self.accel_group = Gtk.AccelGroup()
46
79
self.add_accel_group(self.accel_group)
81
if getattr(Gtk.Action, 'set_tool_item_type', None) is not None:
82
Gtk.Action.set_tool_item_type(Gtk.MenuToolButton)
84
self.prev_rev_action = Gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", Gtk.STOCK_GO_DOWN)
85
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
86
self.prev_rev_action.set_accel_group(self.accel_group)
87
self.prev_rev_action.connect("activate", self._back_clicked_cb)
88
self.prev_rev_action.connect_accelerator()
90
self.next_rev_action = Gtk.Action("next-rev", "_Next Revision", "Go to the next revision", Gtk.STOCK_GO_UP)
91
self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
92
self.next_rev_action.set_accel_group(self.accel_group)
93
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
94
self.next_rev_action.connect_accelerator()
96
self.refresh_action = Gtk.Action("refresh", "_Refresh", "Refresh view", Gtk.STOCK_REFRESH)
97
self.refresh_action.set_accel_path("<viz>/View/Refresh")
98
self.refresh_action.set_accel_group(self.accel_group)
99
self.refresh_action.connect("activate", self._refresh_clicked)
100
self.refresh_action.connect_accelerator()
102
self.vbox = self.construct()
104
def _save_size_on_destroy(self, widget, config_name):
105
"""Creates a hook that saves the size of widget to config option
106
config_name when the window is destroyed/closed."""
108
allocation = widget.get_allocation()
109
width, height = allocation.width, allocation.height
110
value = '%sx%s' % (width, height)
111
self.config.set_user_option(config_name, value)
112
self.connect("destroy", save_size)
114
def set_revision(self, revid):
115
self.treeview.set_revision_id(revid)
50
117
def construct(self):
51
118
"""Construct the window contents."""
52
vbox = gtk.VBox(spacing=0)
119
vbox = Gtk.VBox(spacing=0)
55
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
122
# order is important here
123
paned = self.construct_paned()
124
nav = self.construct_navigation()
125
menubar = self.construct_menubar()
58
paned.pack1(self.construct_top(), resize=True, shrink=False)
59
paned.pack2(self.construct_bottom(), resize=False, shrink=True)
61
vbox.pack_start(paned, expand=True, fill=True)
127
vbox.pack_start(menubar, False, True, 0)
128
vbox.pack_start(nav, False, True, 0)
129
vbox.pack_start(paned, True, True, 0)
62
130
vbox.set_focus_child(paned)
137
def construct_paned(self):
138
"""Construct the main HPaned/VPaned contents."""
139
if self.config.get_user_option('viz-vertical') == 'True':
140
self.paned = Gtk.HPaned()
142
self.paned = Gtk.VPaned()
144
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
145
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
150
def construct_menubar(self):
151
menubar = Gtk.MenuBar()
153
file_menu = Gtk.Menu()
154
file_menuitem = Gtk.MenuItem.new_with_mnemonic("_File")
155
file_menuitem.set_submenu(file_menu)
157
file_menu_close = Gtk.ImageMenuItem.new_from_stock(
158
Gtk.STOCK_CLOSE, self.accel_group)
159
file_menu_close.connect('activate', lambda x: self.destroy())
161
file_menu_quit = Gtk.ImageMenuItem.new_from_stock(
162
Gtk.STOCK_QUIT, self.accel_group)
163
file_menu_quit.connect('activate', lambda x: Gtk.main_quit())
165
if self._parent is not None:
166
file_menu.add(file_menu_close)
167
file_menu.add(file_menu_quit)
169
edit_menu = Gtk.Menu()
170
edit_menuitem = Gtk.MenuItem.new_with_mnemonic("_Edit")
171
edit_menuitem.set_submenu(edit_menu)
173
edit_menu_branchopts = Gtk.MenuItem(label="Branch Settings")
174
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
176
edit_menu_globopts = Gtk.MenuItem(label="Global Settings")
177
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
179
edit_menu.add(edit_menu_branchopts)
180
edit_menu.add(edit_menu_globopts)
182
view_menu = Gtk.Menu()
183
view_menuitem = Gtk.MenuItem.new_with_mnemonic("_View")
184
view_menuitem.set_submenu(view_menu)
186
view_menu_refresh = self.refresh_action.create_menu_item()
187
view_menu_refresh.connect('activate', self._refresh_clicked)
189
view_menu.add(view_menu_refresh)
190
view_menu.add(Gtk.SeparatorMenuItem())
192
view_menu_toolbar = Gtk.CheckMenuItem(label="Show Toolbar")
193
view_menu_toolbar.set_active(True)
194
if self.config.get_user_option('viz-toolbar-visible') == 'False':
195
view_menu_toolbar.set_active(False)
197
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
199
view_menu_compact = Gtk.CheckMenuItem(label="Show Compact Graph")
200
view_menu_compact.set_active(self.compact_view)
201
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
203
view_menu_vertical = Gtk.CheckMenuItem(label="Side-by-side Layout")
204
view_menu_vertical.set_active(False)
205
if self.config.get_user_option('viz-vertical') == 'True':
206
view_menu_vertical.set_active(True)
207
view_menu_vertical.connect('toggled', self._vertical_layout)
209
view_menu_diffs = Gtk.CheckMenuItem(label="Show Diffs")
210
view_menu_diffs.set_active(False)
211
if self.config.get_user_option('viz-show-diffs') == 'True':
212
view_menu_diffs.set_active(True)
213
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
215
view_menu_wide_diffs = Gtk.CheckMenuItem(label="Wide Diffs")
216
view_menu_wide_diffs.set_active(False)
217
if self.config.get_user_option('viz-wide-diffs') == 'True':
218
view_menu_wide_diffs.set_active(True)
219
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
221
view_menu_wrap_diffs = Gtk.CheckMenuItem.new_with_mnemonic(
222
"Wrap _Long Lines in Diffs")
223
view_menu_wrap_diffs.set_active(False)
224
if self.config.get_user_option('viz-wrap-diffs') == 'True':
225
view_menu_wrap_diffs.set_active(True)
226
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
228
view_menu.add(view_menu_toolbar)
229
view_menu.add(view_menu_compact)
230
view_menu.add(view_menu_vertical)
231
view_menu.add(Gtk.SeparatorMenuItem())
232
view_menu.add(view_menu_diffs)
233
view_menu.add(view_menu_wide_diffs)
234
view_menu.add(view_menu_wrap_diffs)
235
view_menu.add(Gtk.SeparatorMenuItem())
237
self.mnu_show_revno_column = Gtk.CheckMenuItem.new_with_mnemonic(
238
"Show Revision _Number Column")
239
self.mnu_show_date_column = Gtk.CheckMenuItem.new_with_mnemonic(
242
# Revision numbers are pointless if there are multiple branches
243
if len(self.start_revs) > 1:
244
self.mnu_show_revno_column.set_sensitive(False)
245
self.treeview.set_property('revno-column-visible', False)
247
for (col, name) in [(self.mnu_show_revno_column, "revno"),
248
(self.mnu_show_date_column, "date")]:
249
col.set_active(self.treeview.get_property(name + "-column-visible"))
250
col.connect('toggled', self._col_visibility_changed, name)
254
go_menu.set_accel_group(self.accel_group)
255
go_menuitem = Gtk.MenuItem.new_with_mnemonic("_Go")
256
go_menuitem.set_submenu(go_menu)
258
go_menu_next = self.next_rev_action.create_menu_item()
259
go_menu_prev = self.prev_rev_action.create_menu_item()
261
tag_image = Gtk.Image()
262
tag_image.set_from_file(icon_path("tag-16.png"))
263
self.go_menu_tags = Gtk.ImageMenuItem.new_with_mnemonic("_Tags")
264
self.go_menu_tags.set_image(tag_image)
265
self.treeview.connect('refreshed', lambda w: self._update_tags())
267
go_menu.add(go_menu_next)
268
go_menu.add(go_menu_prev)
269
go_menu.add(Gtk.SeparatorMenuItem())
270
go_menu.add(self.go_menu_tags)
272
self.revision_menu = RevisionMenu(self.branch.repository, [],
273
self.branch, parent=self)
274
revision_menuitem = Gtk.MenuItem.new_with_mnemonic("_Revision")
275
revision_menuitem.set_submenu(self.revision_menu)
277
branch_menu = Gtk.Menu()
278
branch_menuitem = Gtk.MenuItem.new_with_mnemonic("_Branch")
279
branch_menuitem.set_submenu(branch_menu)
281
branch_menu.add(Gtk.MenuItem.new_with_mnemonic("Pu_ll Revisions"))
282
branch_menu.add(Gtk.MenuItem.new_with_mnemonic("Pu_sh Revisions"))
285
from bzrlib.plugins import search
287
mutter("Didn't find search plugin")
289
branch_menu.add(Gtk.SeparatorMenuItem())
291
branch_index_menuitem = Gtk.MenuItem.new_with_mnemonic("_Index")
292
branch_index_menuitem.connect('activate', self._branch_index_cb)
293
branch_menu.add(branch_index_menuitem)
295
branch_search_menuitem = Gtk.MenuItem.new_with_mnemonic("_Search")
296
branch_search_menuitem.connect('activate', self._branch_search_cb)
297
branch_menu.add(branch_search_menuitem)
299
help_menu = Gtk.Menu()
300
help_menuitem = Gtk.MenuItem.new_with_mnemonic("_Help")
301
help_menuitem.set_submenu(help_menu)
303
help_about_menuitem = Gtk.ImageMenuItem.new_from_stock(
304
Gtk.STOCK_ABOUT, self.accel_group)
305
help_about_menuitem.connect('activate', self._about_dialog_cb)
307
help_menu.add(help_about_menuitem)
309
menubar.add(file_menuitem)
310
menubar.add(edit_menuitem)
311
menubar.add(view_menuitem)
312
menubar.add(go_menuitem)
313
menubar.add(revision_menuitem)
314
menubar.add(branch_menuitem)
315
menubar.add(help_menuitem)
66
320
def construct_top(self):
67
321
"""Construct the top-half of the window."""
68
scrollwin = gtk.ScrolledWindow()
69
scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
70
scrollwin.set_shadow_type(gtk.SHADOW_IN)
73
self.treeview = gtk.TreeView()
74
self.treeview.set_rules_hint(True)
75
self.treeview.set_search_column(4)
76
self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
77
self.treeview.connect("row-activated", self._treeview_row_activated_cb)
78
self.treeview.connect("button-release-event",
79
self._treeview_row_mouseclick)
80
scrollwin.add(self.treeview)
322
# FIXME: Make broken_line_length configurable
324
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum,
327
for col in ["revno", "date"]:
328
option = self.config.get_user_option(col + '-column-visible')
329
if option is not None:
330
self.treeview.set_property(col + '-column-visible',
333
self.treeview.set_property(col + '-column-visible', False)
81
335
self.treeview.show()
83
cell = CellRendererGraph()
84
column = gtk.TreeViewColumn()
85
column.set_resizable(True)
86
column.pack_start(cell, expand=False)
87
column.add_attribute(cell, "node", 1)
88
column.add_attribute(cell, "in-lines", 2)
89
column.add_attribute(cell, "out-lines", 3)
90
self.treeview.append_column(column)
92
cell = gtk.CellRendererText()
93
cell.set_property("width-chars", 40)
94
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
95
column = gtk.TreeViewColumn("Message")
96
column.set_resizable(True)
97
column.pack_start(cell, expand=True)
98
column.add_attribute(cell, "text", 4)
99
self.treeview.append_column(column)
101
cell = gtk.CellRendererText()
102
cell.set_property("width-chars", 40)
103
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
104
column = gtk.TreeViewColumn("Committer")
105
column.set_resizable(True)
106
column.pack_start(cell, expand=True)
107
column.add_attribute(cell, "text", 5)
108
self.treeview.append_column(column)
110
cell = gtk.CellRendererText()
111
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
112
column = gtk.TreeViewColumn("Date")
113
column.set_resizable(True)
114
column.pack_start(cell, expand=True)
115
column.add_attribute(cell, "text", 6)
116
self.treeview.append_column(column)
337
align = Gtk.Alignment.new(0.0, 0.0, 1.0, 1.0)
338
align.set_padding(5, 0, 0, 0)
339
align.add(self.treeview)
340
# user-configured size
341
size = self._load_size('viz-graph-size')
344
align.set_size_request(width, height)
346
(width, height) = self.get_size()
347
align.set_size_request(width, int(height / 2.5))
348
self._save_size_on_destroy(align, 'viz-graph-size')
120
353
def construct_navigation(self):
121
354
"""Construct the navigation buttons."""
123
frame.set_shadow_type(gtk.SHADOW_OUT)
126
hbox = gtk.HBox(spacing=12)
130
self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
131
self.back_button.set_relief(gtk.RELIEF_NONE)
132
self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
134
self.back_button.connect("clicked", self._back_clicked_cb)
135
hbox.pack_start(self.back_button, expand=False, fill=True)
136
self.back_button.show()
138
self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
139
self.fwd_button.set_relief(gtk.RELIEF_NONE)
140
self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
142
self.fwd_button.connect("clicked", self._fwd_clicked_cb)
143
hbox.pack_start(self.fwd_button, expand=False, fill=True)
144
self.fwd_button.show()
355
self.toolbar = Gtk.Toolbar()
356
self.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)
358
self.prev_button = self.prev_rev_action.create_tool_item()
359
self.toolbar.insert(self.prev_button, -1)
361
self.next_button = self.next_rev_action.create_tool_item()
362
self.toolbar.insert(self.next_button, -1)
364
self.toolbar.insert(Gtk.SeparatorToolItem(), -1)
366
refresh_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_REFRESH)
367
refresh_button.connect('clicked', self._refresh_clicked)
368
self.toolbar.insert(refresh_button, -1)
370
self.toolbar.show_all()
148
374
def construct_bottom(self):
149
375
"""Construct the bottom half of the window."""
150
from bzrlib.plugins.gtk.logview import LogView
151
self.logview = LogView()
376
if self.config.get_user_option('viz-wide-diffs') == 'True':
377
self.diff_paned = Gtk.VPaned()
379
self.diff_paned = Gtk.HPaned()
152
380
(width, height) = self.get_size()
153
self.logview.set_size_request(width, int(height / 2.5))
155
self.logview.set_show_callback(self._show_clicked_cb)
156
self.logview.set_go_callback(self._go_clicked_cb)
159
def set_branch(self, branch, start, maxnum):
160
"""Set the branch and start position for this window.
162
Creates a new TreeModel and populates it with information about
163
the new branch before updating the window title and model of the
168
# [ revision, node, last_lines, lines, message, committer, timestamp ]
169
self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
170
gobject.TYPE_PYOBJECT,
171
gobject.TYPE_PYOBJECT,
172
gobject.TYPE_PYOBJECT,
178
(self.revisions, colours, self.children, self.parent_ids,
179
merge_sorted) = distances(branch.repository, start)
180
for (index, (revision, node, lines)) in enumerate(graph(
181
self.revisions, colours, merge_sorted)):
182
# FIXME: at this point we should be able to show the graph order
183
# and lines with no message or commit data - and then incrementally
184
# fill the timestamp, committer etc data as desired.
185
message = revision.message.split("\n")[0]
186
if revision.committer is not None:
187
timestamp = format_date(revision.timestamp, revision.timezone)
190
self.model.append([revision, node, last_lines, lines,
191
message, revision.committer, timestamp])
192
self.index[revision] = index
194
if maxnum is not None and index > maxnum:
197
self.set_title(branch.nick + " - bzrk")
198
self.treeview.set_model(self.model)
200
def _treeview_cursor_cb(self, *args):
201
"""Callback for when the treeview cursor changes."""
202
(path, col) = self.treeview.get_cursor()
203
revision = self.model[path][0]
205
self.back_button.set_sensitive(len(self.parent_ids[revision]) > 0)
206
self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
208
if self.branch.supports_tags():
209
tagdict = self.branch.tags.get_reverse_tag_dict()
210
if tagdict.has_key(revision.revision_id):
211
tags = tagdict[revision.revision_id]
212
self.logview.set_revision(revision, tags)
381
self.diff_paned.set_size_request(20, 20) # shrinkable
383
from bzrlib.plugins.gtk.revisionview import RevisionView
384
self.revisionview = RevisionView(branch=self.branch)
385
self.revisionview.set_size_request(width/3, int(height / 2.5))
386
# user-configured size
387
size = self._load_size('viz-revisionview-size')
390
self.revisionview.set_size_request(width, height)
391
self._save_size_on_destroy(self.revisionview, 'viz-revisionview-size')
392
self.revisionview.show()
393
self.revisionview.set_show_callback(self._show_clicked_cb)
394
self.revisionview.connect('notify::revision', self._go_clicked_cb)
395
self.treeview.connect('tag-added',
396
lambda w, t, r: self.revisionview.update_tags())
397
self.treeview.connect('revision-selected',
398
self._treeselection_changed_cb)
399
self.treeview.connect('revision-activated',
400
self._tree_revision_activated)
401
self.diff_paned.pack1(self.revisionview)
403
from bzrlib.plugins.gtk.diff import DiffWidget
404
self.diff = DiffWidget()
405
self.diff_paned.pack2(self.diff)
407
self.diff_paned.show_all()
408
if self.config.get_user_option('viz-show-diffs') != 'True':
411
return self.diff_paned
413
def _tag_selected_cb(self, menuitem, revid):
414
self.treeview.set_revision_id(revid)
416
def _treeselection_changed_cb(self, selection, *args):
417
"""callback for when the treeview changes."""
418
revision = self.treeview.get_revision()
419
parents = self.treeview.get_parents()
420
children = self.treeview.get_children()
422
if revision and revision.revision_id != NULL_REVISION:
423
self.revision_menu.set_revision_ids([revision.revision_id])
424
prev_menu = Gtk.Menu()
426
self.prev_rev_action.set_sensitive(True)
427
for parent_id in parents:
428
if parent_id and parent_id != NULL_REVISION:
429
parent = self.branch.repository.get_revision(parent_id)
431
str = ' (%s)' % parent.properties['branch-nick']
436
label=parent.message.split("\n")[0] + str)
437
item.connect('activate', self._set_revision_cb, parent_id)
441
self.prev_rev_action.set_sensitive(False)
444
if getattr(self.prev_button, 'set_menu', None) is not None:
445
self.prev_button.set_menu(prev_menu)
447
next_menu = Gtk.Menu()
448
if len(children) > 0:
449
self.next_rev_action.set_sensitive(True)
450
for child_id in children:
451
child = self.branch.repository.get_revision(child_id)
453
str = ' (%s)' % child.properties['branch-nick']
458
label=child.message.split("\n")[0] + str)
459
item.connect('activate', self._set_revision_cb, child_id)
463
self.next_rev_action.set_sensitive(False)
466
if getattr(self.next_button, 'set_menu', None) is not None:
467
self.next_button.set_menu(next_menu)
469
self.revisionview.set_revision(revision)
470
self.revisionview.set_children(children)
471
self.update_diff_panel(revision, parents)
473
def _tree_revision_activated(self, widget, path, col):
474
# TODO: more than one parent
475
"""Callback for when a treeview row gets activated."""
476
revision = self.treeview.get_revision()
477
parents = self.treeview.get_parents()
479
if len(parents) == 0:
480
parent_id = NULL_REVISION
482
parent_id = parents[0]
484
self.show_diff(revision.revision_id, parent_id)
485
self.treeview.grab_focus()
214
487
def _back_clicked_cb(self, *args):
215
488
"""Callback for when the back button is clicked."""
216
(path, col) = self.treeview.get_cursor()
217
revision = self.model[path][0]
218
if not len(self.parent_ids[revision]):
221
for parent_id in self.parent_ids[revision]:
222
parent = self.revisions[parent_id]
223
if same_branch(revision, parent):
224
self.treeview.set_cursor(self.index[parent])
227
next = self.revisions[self.parent_ids[revision][0]]
228
self.treeview.set_cursor(self.index[next])
229
self.treeview.grab_focus()
231
491
def _fwd_clicked_cb(self, *args):
232
492
"""Callback for when the forward button is clicked."""
233
(path, col) = self.treeview.get_cursor()
234
revision = self.model[path][0]
235
if not len(self.children[revision]):
238
for child in self.children[revision]:
239
if same_branch(child, revision):
240
self.treeview.set_cursor(self.index[child])
243
prev = list(self.children[revision])[0]
244
self.treeview.set_cursor(self.index[prev])
245
self.treeview.grab_focus()
247
def _go_clicked_cb(self, revid):
493
self.treeview.forward()
495
def _go_clicked_cb(self, w, p):
248
496
"""Callback for when the go button for a parent is clicked."""
249
self.treeview.set_cursor(self.index[self.revisions[revid]])
497
if self.revisionview.get_revision() is not None:
498
self.treeview.set_revision(self.revisionview.get_revision())
500
def _show_clicked_cb(self, revid, parentid):
501
"""Callback for when the show button for a parent is clicked."""
502
self.show_diff(revid, parentid)
250
503
self.treeview.grab_focus()
252
def show_diff(self, branch, revid, parentid):
505
def _set_revision_cb(self, w, revision_id):
506
self.treeview.set_revision_id(revision_id)
508
def _brokenlines_toggled_cb(self, button):
509
self.compact_view = button.get_active()
511
if self.compact_view:
516
self.config.set_user_option('viz-compact-view', option)
517
self.treeview.set_property('compact', self.compact_view)
518
self.treeview.refresh()
520
def _branch_index_cb(self, w):
521
from bzrlib.plugins.search import index as _mod_index
522
_mod_index.index_url(self.branch.base)
524
def _branch_search_cb(self, w):
525
from bzrlib.plugins.search import (
527
errors as search_errors,
529
from bzrlib.plugins.gtk.search import SearchDialog
532
index = _mod_index.open_index_url(self.branch.base)
533
except search_errors.NoSearchIndex:
534
dialog = Gtk.MessageDialog(self, type=Gtk.MessageType.QUESTION,
535
buttons=Gtk.ButtonsType.OK_CANCEL,
536
message_format="This branch has not been indexed yet. "
538
if dialog.run() == Gtk.ResponseType.OK:
540
index = _mod_index.index_url(self.branch.base)
545
dialog = SearchDialog(index)
547
if dialog.run() == Gtk.ResponseType.OK:
548
self.set_revision(dialog.get_revision())
552
def _about_dialog_cb(self, w):
553
from bzrlib.plugins.gtk.about import AboutDialog
556
def _col_visibility_changed(self, col, property):
557
self.config.set_user_option(property + '-column-visible', col.get_active())
558
self.treeview.set_property(property + '-column-visible', col.get_active())
560
def _toolbar_visibility_changed(self, col):
565
self.config.set_user_option('viz-toolbar-visible', col.get_active())
567
def _vertical_layout(self, col):
568
"""Toggle the layout vertical/horizontal"""
569
self.config.set_user_option('viz-vertical', str(col.get_active()))
572
self.vbox.remove(old)
573
self.vbox.pack_start(
574
self.construct_paned(), True, True, True, 0)
575
self._make_diff_paned_nonzero_size()
576
self._make_diff_nonzero_size()
578
self.treeview.emit('revision-selected')
580
def _make_diff_paned_nonzero_size(self):
581
"""make sure the diff/revision pane isn't zero-width or zero-height"""
582
alloc = self.diff_paned.get_allocation()
583
if (alloc.width < 10) or (alloc.height < 10):
584
width, height = self.get_size()
585
self.diff_paned.set_size_request(width/3, int(height / 2.5))
587
def _make_diff_nonzero_size(self):
588
"""make sure the diff isn't zero-width or zero-height"""
589
alloc = self.diff.get_allocation()
590
if (alloc.width < 10) or (alloc.height < 10):
591
width, height = self.get_size()
592
self.revisionview.set_size_request(width/3, int(height / 2.5))
594
def _diff_visibility_changed(self, col):
595
"""Hide or show the diff panel."""
598
self._make_diff_nonzero_size()
601
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
602
self.update_diff_panel()
604
def _diff_placement_changed(self, col):
605
"""Toggle the diff panel's position."""
606
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
608
old = self.paned.get_child2()
609
self.paned.remove(old)
610
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
611
self._make_diff_nonzero_size()
613
self.treeview.emit('revision-selected')
615
def _diff_wrap_changed(self, widget):
616
"""Toggle word wrap in the diff widget."""
617
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
618
self.diff._on_wraplines_toggled(widget)
620
def _refresh_clicked(self, w):
621
self.treeview.refresh()
623
def _update_tags(self):
626
if self.branch.supports_tags():
627
tags = self.branch.tags.get_tag_dict().items()
628
tags.sort(reverse=True)
629
for tag, revid in tags:
630
tag_image = Gtk.Image()
631
tag_image.set_from_file(icon_path('tag-16.png'))
632
tag_item = Gtk.ImageMenuItem.new_with_mnemonic(
633
tag.replace('_', '__'))
634
tag_item.set_image(tag_image)
635
tag_item.connect('activate', self._tag_selected_cb, revid)
636
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
638
self.go_menu_tags.set_submenu(menu)
640
self.go_menu_tags.set_sensitive(len(tags) != 0)
642
self.go_menu_tags.set_sensitive(False)
644
self.go_menu_tags.show_all()
646
def _load_size(self, name):
647
"""Read and parse 'name' from self.config.
648
The value is a string, formatted as WIDTHxHEIGHT
649
Returns None, or (width, height)
651
size = self.config.get_user_option(name)
653
width, height = [int(num) for num in size.split('x')]
654
# avoid writing config every time we start
658
def show_diff(self, revid=None, parentid=NULL_REVISION):
253
659
"""Open a new window to show a diff between the given revisions."""
254
660
from bzrlib.plugins.gtk.diff import DiffWindow
255
window = DiffWindow()
256
(parent_tree, rev_tree) = branch.repository.revision_trees([parentid,
258
description = revid + " - " + branch.nick
661
window = DiffWindow(parent=self)
663
rev_tree = self.branch.repository.revision_tree(revid)
664
parent_tree = self.branch.repository.revision_tree(parentid)
666
description = revid + " - " + self.branch._get_nick(local=True)
259
667
window.set_diff(description, rev_tree, parent_tree)
262
def _show_clicked_cb(self, revid, parentid):
263
"""Callback for when the show button for a parent is clicked."""
264
self.show_diff(self.branch, revid, parentid)
265
self.treeview.grab_focus()
267
def _treeview_row_mouseclick(self, widget, event):
268
from bzrlib.plugins.gtk.revisionmenu import RevisionPopupMenu
269
if event.button == 3:
270
menu = RevisionPopupMenu(self.branch.repository,
271
[x.revision_id for x in self.selected_revisions()],
273
menu.popup(None, None, None, event.button, event.get_time())
275
def selected_revision(self, path):
276
return self.model[path][0]
278
def selected_revisions(self):
279
return [self.selected_revision(path) for path in \
280
self.treeview.get_selection().get_selected_rows()[1]]
282
def _treeview_row_activated_cb(self, widget, path, col):
283
# TODO: more than one parent
284
"""Callback for when a treeview row gets activated."""
285
revision = self.selected_revision(path)
286
if len(self.parent_ids[revision]) == 0:
287
# Ignore revisions without parent
289
parent_id = self.parent_ids[revision][0]
290
self.show_diff(self.branch, revision.revision_id, parent_id)
291
self.treeview.grab_focus()
670
def update_diff_panel(self, revision=None, parents=None):
671
"""Show the current revision in the diff panel."""
672
if self.config.get_user_option('viz-show-diffs') != 'True':
675
if not revision: # default to selected row
676
revision = self.treeview.get_revision()
677
if revision == NULL_REVISION:
680
if not parents: # default to selected row's parents
681
parents = self.treeview.get_parents()
682
if len(parents) == 0:
683
parent_id = NULL_REVISION
685
parent_id = parents[0]
687
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
688
parent_tree = self.branch.repository.revision_tree(parent_id)
690
self.diff.set_diff(rev_tree, parent_tree)
691
if self.config.get_user_option('viz-wrap-diffs') == 'True':
692
self.diff._on_wraplines_toggled(wrap=True)