1
# -*- coding: UTF-8 -*-
3
4
This module contains the code to manage the branch information window,
4
5
which contains both the revision graph and details panes.
7
__copyright__ = "Copyright (c) 2005 Canonical Ltd."
8
__copyright__ = "Copyright © 2005 Canonical Ltd."
8
9
__author__ = "Scott James Remnant <scott@ubuntu.com>"
13
from bzrlib.plugins.gtk import icon_path
14
from bzrlib.plugins.gtk.branchview import TreeView
15
from bzrlib.plugins.gtk.preferences import PreferencesWindow
16
from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
17
from bzrlib.plugins.gtk.window import Window
19
from bzrlib.config import GlobalConfig
20
from bzrlib.revision import NULL_REVISION
21
from bzrlib.trace import mutter
23
class BranchWindow(Window):
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):
26
25
This object represents and manages a single window containing information
27
26
for a particular branch.
30
def __init__(self, branch, start_revs, maxnum, parent=None):
31
"""Create a new BranchWindow.
33
:param branch: Branch object for branch to show.
34
:param start_revs: Revision ids of top revisions.
35
:param maxnum: Maximum number of revisions to display,
39
Window.__init__(self, parent=parent)
30
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
40
31
self.set_border_width(0)
43
self.start_revs = start_revs
45
self.config = GlobalConfig()
47
if self.config.get_user_option('viz-compact-view') == 'yes':
48
self.compact_view = True
50
self.compact_view = False
52
self.set_title(branch._get_nick(local=True) + " - revision history")
54
# user-configured window size
55
size = self._load_size('viz-window-size')
59
# Use three-quarters of the screen by default
60
screen = self.get_screen()
61
monitor = screen.get_monitor_geometry(0)
62
width = int(monitor.width * 0.75)
63
height = int(monitor.height * 0.75)
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)
64
39
self.set_default_size(width, height)
65
self.set_size_request(width/3, height/3)
66
self._save_size_on_destroy(self, 'viz-window-size')
69
42
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
70
43
self.set_icon(icon)
72
gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
73
gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
74
gtk.accel_map_add_entry("<viz>/View/Refresh", gtk.keysyms.F5, 0)
76
45
self.accel_group = gtk.AccelGroup()
77
46
self.add_accel_group(self.accel_group)
79
if getattr(gtk.Action, 'set_tool_item_type', None) is not None:
80
# Not available before PyGtk-2.10
81
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
83
self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
84
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
85
self.prev_rev_action.set_accel_group(self.accel_group)
86
self.prev_rev_action.connect("activate", self._back_clicked_cb)
87
self.prev_rev_action.connect_accelerator()
89
self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
90
self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
91
self.next_rev_action.set_accel_group(self.accel_group)
92
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
93
self.next_rev_action.connect_accelerator()
95
self.refresh_action = gtk.Action("refresh", "_Refresh", "Refresh view", gtk.STOCK_REFRESH)
96
self.refresh_action.set_accel_path("<viz>/View/Refresh")
97
self.refresh_action.set_accel_group(self.accel_group)
98
self.refresh_action.connect("activate", self._refresh_clicked)
99
self.refresh_action.connect_accelerator()
103
def _save_size_on_destroy(self, widget, config_name):
104
"""Creates a hook that saves the size of widget to config option
105
config_name when the window is destroyed/closed."""
107
width, height = widget.allocation.width, widget.allocation.height
108
value = '%sx%s' % (width, height)
109
self.config.set_user_option(config_name, value)
110
self.connect("destroy", save_size)
112
def set_revision(self, revid):
113
self.treeview.set_revision_id(revid)
115
50
def construct(self):
116
51
"""Construct the window contents."""
117
52
vbox = gtk.VBox(spacing=0)
120
self.paned = gtk.VPaned()
121
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
122
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
125
nav = self.construct_navigation()
126
menubar = self.construct_menubar()
127
vbox.pack_start(menubar, expand=False, fill=True)
128
vbox.pack_start(nav, expand=False, fill=True)
130
vbox.pack_start(self.paned, expand=True, fill=True)
131
vbox.set_focus_child(self.paned)
133
self.treeview.connect('revision-selected',
134
self._treeselection_changed_cb)
135
self.treeview.connect('revision-activated',
136
self._tree_revision_activated)
138
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
55
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
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)
62
vbox.set_focus_child(paned)
141
def construct_menubar(self):
142
menubar = gtk.MenuBar()
144
file_menu = gtk.Menu()
145
file_menuitem = gtk.MenuItem("_File")
146
file_menuitem.set_submenu(file_menu)
148
file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
149
file_menu_close.connect('activate', lambda x: self.destroy())
151
file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
152
file_menu_quit.connect('activate', lambda x: gtk.main_quit())
154
if self._parent is not None:
155
file_menu.add(file_menu_close)
156
file_menu.add(file_menu_quit)
158
edit_menu = gtk.Menu()
159
edit_menuitem = gtk.MenuItem("_Edit")
160
edit_menuitem.set_submenu(edit_menu)
162
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
163
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
165
edit_menu_globopts = gtk.MenuItem("Global Settings")
166
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
168
edit_menu.add(edit_menu_branchopts)
169
edit_menu.add(edit_menu_globopts)
171
view_menu = gtk.Menu()
172
view_menuitem = gtk.MenuItem("_View")
173
view_menuitem.set_submenu(view_menu)
175
view_menu_refresh = self.refresh_action.create_menu_item()
176
view_menu_refresh.connect('activate', self._refresh_clicked)
178
view_menu.add(view_menu_refresh)
179
view_menu.add(gtk.SeparatorMenuItem())
181
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
182
view_menu_toolbar.set_active(True)
183
if self.config.get_user_option('viz-toolbar-visible') == 'False':
184
view_menu_toolbar.set_active(False)
186
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
188
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
189
view_menu_compact.set_active(self.compact_view)
190
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
192
view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
193
view_menu_diffs.set_active(False)
194
if self.config.get_user_option('viz-show-diffs') == 'True':
195
view_menu_diffs.set_active(True)
196
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
198
view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
199
view_menu_wide_diffs.set_active(False)
200
if self.config.get_user_option('viz-wide-diffs') == 'True':
201
view_menu_wide_diffs.set_active(True)
202
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
204
view_menu_wrap_diffs = gtk.CheckMenuItem("Wrap _Long Lines in Diffs")
205
view_menu_wrap_diffs.set_active(False)
206
if self.config.get_user_option('viz-wrap-diffs') == 'True':
207
view_menu_wrap_diffs.set_active(True)
208
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
210
view_menu.add(view_menu_toolbar)
211
view_menu.add(view_menu_compact)
212
view_menu.add(gtk.SeparatorMenuItem())
213
view_menu.add(view_menu_diffs)
214
view_menu.add(view_menu_wide_diffs)
215
view_menu.add(view_menu_wrap_diffs)
216
view_menu.add(gtk.SeparatorMenuItem())
218
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
219
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
221
# Revision numbers are pointless if there are multiple branches
222
if len(self.start_revs) > 1:
223
self.mnu_show_revno_column.set_sensitive(False)
224
self.treeview.set_property('revno-column-visible', False)
226
for (col, name) in [(self.mnu_show_revno_column, "revno"),
227
(self.mnu_show_date_column, "date")]:
228
col.set_active(self.treeview.get_property(name + "-column-visible"))
229
col.connect('toggled', self._col_visibility_changed, name)
233
go_menu.set_accel_group(self.accel_group)
234
go_menuitem = gtk.MenuItem("_Go")
235
go_menuitem.set_submenu(go_menu)
237
go_menu_next = self.next_rev_action.create_menu_item()
238
go_menu_prev = self.prev_rev_action.create_menu_item()
240
tag_image = gtk.Image()
241
tag_image.set_from_file(icon_path("tag-16.png"))
242
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
243
self.go_menu_tags.set_image(tag_image)
244
self.treeview.connect('refreshed', lambda w: self._update_tags())
246
go_menu.add(go_menu_next)
247
go_menu.add(go_menu_prev)
248
go_menu.add(gtk.SeparatorMenuItem())
249
go_menu.add(self.go_menu_tags)
251
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
252
revision_menuitem = gtk.MenuItem("_Revision")
253
revision_menuitem.set_submenu(self.revision_menu)
255
branch_menu = gtk.Menu()
256
branch_menuitem = gtk.MenuItem("_Branch")
257
branch_menuitem.set_submenu(branch_menu)
259
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
260
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
263
from bzrlib.plugins import search
265
mutter("Didn't find search plugin")
267
branch_menu.add(gtk.SeparatorMenuItem())
269
branch_index_menuitem = gtk.MenuItem("_Index")
270
branch_index_menuitem.connect('activate', self._branch_index_cb)
271
branch_menu.add(branch_index_menuitem)
273
branch_search_menuitem = gtk.MenuItem("_Search")
274
branch_search_menuitem.connect('activate', self._branch_search_cb)
275
branch_menu.add(branch_search_menuitem)
277
help_menu = gtk.Menu()
278
help_menuitem = gtk.MenuItem("_Help")
279
help_menuitem.set_submenu(help_menu)
281
help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
282
help_about_menuitem.connect('activate', self._about_dialog_cb)
284
help_menu.add(help_about_menuitem)
286
menubar.add(file_menuitem)
287
menubar.add(edit_menuitem)
288
menubar.add(view_menuitem)
289
menubar.add(go_menuitem)
290
menubar.add(revision_menuitem)
291
menubar.add(branch_menuitem)
292
menubar.add(help_menuitem)
297
66
def construct_top(self):
298
67
"""Construct the top-half of the window."""
299
# FIXME: Make broken_line_length configurable
301
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
303
for col in ["revno", "date"]:
304
option = self.config.get_user_option(col + '-column-visible')
305
if option is not None:
306
self.treeview.set_property(col + '-column-visible', option == 'True')
308
self.treeview.set_property(col + '-column-visible', False)
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
scrollwin.add(self.treeview)
310
79
self.treeview.show()
312
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
313
align.set_padding(5, 0, 0, 0)
314
align.add(self.treeview)
315
# user-configured size
316
size = self._load_size('viz-graph-size')
319
align.set_size_request(width, height)
321
(width, height) = self.get_size()
322
align.set_size_request(width, int(height / 2.5))
323
self._save_size_on_destroy(align, 'viz-graph-size')
81
cell = CellRendererGraph()
82
column = gtk.TreeViewColumn()
83
column.set_resizable(True)
84
column.pack_start(cell, expand=False)
85
column.add_attribute(cell, "node", 1)
86
column.add_attribute(cell, "in-lines", 2)
87
column.add_attribute(cell, "out-lines", 3)
88
self.treeview.append_column(column)
90
cell = gtk.CellRendererText()
91
cell.set_property("width-chars", 40)
92
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
93
column = gtk.TreeViewColumn("Message")
94
column.set_resizable(True)
95
column.pack_start(cell, expand=True)
96
column.add_attribute(cell, "text", 4)
97
self.treeview.append_column(column)
99
cell = gtk.CellRendererText()
100
cell.set_property("width-chars", 40)
101
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
102
column = gtk.TreeViewColumn("Committer")
103
column.set_resizable(True)
104
column.pack_start(cell, expand=True)
105
column.add_attribute(cell, "text", 5)
106
self.treeview.append_column(column)
108
cell = gtk.CellRendererText()
109
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
110
column = gtk.TreeViewColumn("Date")
111
column.set_resizable(True)
112
column.pack_start(cell, expand=True)
113
column.add_attribute(cell, "text", 6)
114
self.treeview.append_column(column)
328
118
def construct_navigation(self):
329
119
"""Construct the navigation buttons."""
330
self.toolbar = gtk.Toolbar()
331
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
333
self.prev_button = self.prev_rev_action.create_tool_item()
334
self.toolbar.insert(self.prev_button, -1)
336
self.next_button = self.next_rev_action.create_tool_item()
337
self.toolbar.insert(self.next_button, -1)
339
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
341
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
342
refresh_button.connect('clicked', self._refresh_clicked)
343
self.toolbar.insert(refresh_button, -1)
345
self.toolbar.show_all()
121
frame.set_shadow_type(gtk.SHADOW_OUT)
124
hbox = gtk.HBox(spacing=12)
128
self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
129
self.back_button.set_relief(gtk.RELIEF_NONE)
130
self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
132
self.back_button.connect("clicked", self._back_clicked_cb)
133
hbox.pack_start(self.back_button, expand=False, fill=True)
134
self.back_button.show()
136
self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
137
self.fwd_button.set_relief(gtk.RELIEF_NONE)
138
self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
140
self.fwd_button.connect("clicked", self._fwd_clicked_cb)
141
hbox.pack_start(self.fwd_button, expand=False, fill=True)
142
self.fwd_button.show()
349
146
def construct_bottom(self):
350
147
"""Construct the bottom half of the window."""
351
if self.config.get_user_option('viz-wide-diffs') == 'True':
352
self.diff_paned = gtk.VPaned()
354
self.diff_paned = gtk.HPaned()
148
from bzrlib.plugins.gtk.logview import LogView
149
self.logview = LogView()
150
self.logview.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
151
self.logview.set_shadow_type(gtk.SHADOW_NONE)
355
152
(width, height) = self.get_size()
356
self.diff_paned.set_size_request(20, 20) # shrinkable
358
from bzrlib.plugins.gtk.revisionview import RevisionView
359
self.revisionview = RevisionView(branch=self.branch)
360
self.revisionview.set_size_request(width/3, int(height / 2.5))
361
# user-configured size
362
size = self._load_size('viz-revisionview-size')
365
self.revisionview.set_size_request(width, height)
366
self._save_size_on_destroy(self.revisionview, 'viz-revisionview-size')
367
self.revisionview.show()
368
self.revisionview.set_show_callback(self._show_clicked_cb)
369
self.revisionview.connect('notify::revision', self._go_clicked_cb)
370
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
371
self.diff_paned.pack1(self.revisionview)
373
from bzrlib.plugins.gtk.diff import DiffWidget
374
self.diff = DiffWidget()
375
self.diff_paned.pack2(self.diff)
377
self.diff_paned.show_all()
378
if self.config.get_user_option('viz-show-diffs') != 'True':
381
return self.diff_paned
383
def _tag_selected_cb(self, menuitem, revid):
384
self.treeview.set_revision_id(revid)
386
def _treeselection_changed_cb(self, selection, *args):
387
"""callback for when the treeview changes."""
388
revision = self.treeview.get_revision()
389
parents = self.treeview.get_parents()
390
children = self.treeview.get_children()
392
self.revision_menu.set_revision_ids([revision.revision_id])
394
if revision and revision != NULL_REVISION:
395
prev_menu = gtk.Menu()
397
self.prev_rev_action.set_sensitive(True)
398
for parent_id in parents:
399
if parent_id and parent_id != NULL_REVISION:
400
parent = self.branch.repository.get_revision(parent_id)
402
str = ' (' + parent.properties['branch-nick'] + ')'
406
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
407
item.connect('activate', self._set_revision_cb, parent_id)
411
self.prev_rev_action.set_sensitive(False)
414
if getattr(self.prev_button, 'set_menu', None) is not None:
415
self.prev_button.set_menu(prev_menu)
417
next_menu = gtk.Menu()
418
if len(children) > 0:
419
self.next_rev_action.set_sensitive(True)
420
for child_id in children:
421
child = self.branch.repository.get_revision(child_id)
423
str = ' (' + child.properties['branch-nick'] + ')'
427
item = gtk.MenuItem(child.message.split("\n")[0] + str)
428
item.connect('activate', self._set_revision_cb, child_id)
432
self.next_rev_action.set_sensitive(False)
435
if getattr(self.next_button, 'set_menu', None) is not None:
436
self.next_button.set_menu(next_menu)
438
self.revisionview.set_revision(revision)
439
self.revisionview.set_children(children)
440
self.update_diff_panel(revision, parents)
442
def _tree_revision_activated(self, widget, path, col):
443
# TODO: more than one parent
444
"""Callback for when a treeview row gets activated."""
445
revision = self.treeview.get_revision()
446
parents = self.treeview.get_parents()
448
if len(parents) == 0:
449
parent_id = NULL_REVISION
451
parent_id = parents[0]
453
self.show_diff(revision.revision_id, parent_id)
454
self.treeview.grab_focus()
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, 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)
207
self.logview.set_revision(revision)
456
209
def _back_clicked_cb(self, *args):
457
210
"""Callback for when the back button is clicked."""
211
(path, col) = self.treeview.get_cursor()
212
revision = self.model[path][0]
213
if not len(self.parent_ids[revision]):
216
for parent_id in self.parent_ids[revision]:
217
parent = self.revisions[parent_id]
218
if same_branch(revision, parent):
219
self.treeview.set_cursor(self.index[parent])
222
next = self.revisions[self.parent_ids[revision][0]]
223
self.treeview.set_cursor(self.index[next])
224
self.treeview.grab_focus()
460
226
def _fwd_clicked_cb(self, *args):
461
227
"""Callback for when the forward button is clicked."""
462
self.treeview.forward()
464
def _go_clicked_cb(self, w, p):
228
(path, col) = self.treeview.get_cursor()
229
revision = self.model[path][0]
230
if not len(self.children[revision]):
233
for child in self.children[revision]:
234
if same_branch(child, revision):
235
self.treeview.set_cursor(self.index[child])
238
prev = list(self.children[revision])[0]
239
self.treeview.set_cursor(self.index[prev])
240
self.treeview.grab_focus()
242
def _go_clicked_cb(self, revid):
465
243
"""Callback for when the go button for a parent is clicked."""
466
if self.revisionview.get_revision() is not None:
467
self.treeview.set_revision(self.revisionview.get_revision())
469
def _show_clicked_cb(self, revid, parentid):
470
"""Callback for when the show button for a parent is clicked."""
471
self.show_diff(revid, parentid)
244
self.treeview.set_cursor(self.index[self.revisions[revid]])
472
245
self.treeview.grab_focus()
474
def _set_revision_cb(self, w, revision_id):
475
self.treeview.set_revision_id(revision_id)
477
def _brokenlines_toggled_cb(self, button):
478
self.compact_view = button.get_active()
480
if self.compact_view:
485
self.config.set_user_option('viz-compact-view', option)
486
self.treeview.set_property('compact', self.compact_view)
487
self.treeview.refresh()
489
def _branch_index_cb(self, w):
490
from bzrlib.plugins.search import index as _mod_index
491
_mod_index.index_url(self.branch.base)
493
def _branch_search_cb(self, w):
494
from bzrlib.plugins.search import index as _mod_index
495
from bzrlib.plugins.gtk.search import SearchDialog
496
from bzrlib.plugins.search import errors as search_errors
499
index = _mod_index.open_index_url(self.branch.base)
500
except search_errors.NoSearchIndex:
501
dialog = gtk.MessageDialog(self, type=gtk.MESSAGE_QUESTION,
502
buttons=gtk.BUTTONS_OK_CANCEL,
503
message_format="This branch has not been indexed yet. "
505
if dialog.run() == gtk.RESPONSE_OK:
507
index = _mod_index.index_url(self.branch.base)
512
dialog = SearchDialog(index)
514
if dialog.run() == gtk.RESPONSE_OK:
515
self.set_revision(dialog.get_revision())
519
def _about_dialog_cb(self, w):
520
from bzrlib.plugins.gtk.about import AboutDialog
523
def _col_visibility_changed(self, col, property):
524
self.config.set_user_option(property + '-column-visible', col.get_active())
525
self.treeview.set_property(property + '-column-visible', col.get_active())
527
def _toolbar_visibility_changed(self, col):
532
self.config.set_user_option('viz-toolbar-visible', col.get_active())
534
def _make_diff_nonzero_size(self):
535
"""make sure the diff isn't zero-width or zero-height"""
536
alloc = self.diff.get_allocation()
537
if (alloc.width < 10) or (alloc.height < 10):
538
width, height = self.get_size()
539
self.revisionview.set_size_request(width/3, int(height / 2.5))
541
def _diff_visibility_changed(self, col):
542
"""Hide or show the diff panel."""
545
self._make_diff_nonzero_size()
548
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
549
self.update_diff_panel()
551
def _diff_placement_changed(self, col):
552
"""Toggle the diff panel's position."""
553
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
555
old = self.paned.get_child2()
556
self.paned.remove(old)
557
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
558
self._make_diff_nonzero_size()
560
self.treeview.emit('revision-selected')
562
def _diff_wrap_changed(self, widget):
563
"""Toggle word wrap in the diff widget."""
564
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
565
self.diff._on_wraplines_toggled(widget)
567
def _refresh_clicked(self, w):
568
self.treeview.refresh()
570
def _update_tags(self):
573
if self.branch.supports_tags():
574
tags = self.branch.tags.get_tag_dict().items()
575
tags.sort(reverse=True)
576
for tag, revid in tags:
577
tag_image = gtk.Image()
578
tag_image.set_from_file(icon_path('tag-16.png'))
579
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
580
tag_item.set_image(tag_image)
581
tag_item.connect('activate', self._tag_selected_cb, revid)
582
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
584
self.go_menu_tags.set_submenu(menu)
586
self.go_menu_tags.set_sensitive(len(tags) != 0)
588
self.go_menu_tags.set_sensitive(False)
590
self.go_menu_tags.show_all()
592
def _load_size(self, name):
593
"""Read and parse 'name' from self.config.
594
The value is a string, formatted as WIDTHxHEIGHT
595
Returns None, or (width, height)
597
size = self.config.get_user_option(name)
599
width, height = [int(num) for num in size.split('x')]
600
# avoid writing config every time we start
604
def show_diff(self, revid=None, parentid=NULL_REVISION):
247
def show_diff(self, branch, revid, parentid):
605
248
"""Open a new window to show a diff between the given revisions."""
606
249
from bzrlib.plugins.gtk.diff import DiffWindow
607
window = DiffWindow(parent=self)
609
rev_tree = self.branch.repository.revision_tree(revid)
610
parent_tree = self.branch.repository.revision_tree(parentid)
612
description = revid + " - " + self.branch._get_nick(local=True)
250
window = DiffWindow()
251
rev_tree = branch.repository.revision_tree(revid)
252
parent_tree = branch.repository.revision_tree(parentid)
253
description = revid + " - " + branch.nick
613
254
window.set_diff(description, rev_tree, parent_tree)
616
def update_diff_panel(self, revision=None, parents=None):
617
"""Show the current revision in the diff panel."""
618
if self.config.get_user_option('viz-show-diffs') != 'True':
621
if not revision: # default to selected row
622
revision = self.treeview.get_revision()
623
if revision == NULL_REVISION:
626
if not parents: # default to selected row's parents
627
parents = self.treeview.get_parents()
628
if len(parents) == 0:
629
parent_id = NULL_REVISION
631
parent_id = parents[0]
633
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
634
parent_tree = self.branch.repository.revision_tree(parent_id)
636
self.diff.set_diff(rev_tree, parent_tree)
637
if self.config.get_user_option('viz-wrap-diffs') == 'True':
638
self.diff._on_wraplines_toggled(wrap=True)
257
def _show_clicked_cb(self, revid, parentid):
258
"""Callback for when the show button for a parent is clicked."""
259
self.show_diff(self.branch, revid, parentid)
260
self.treeview.grab_focus()
262
def _treeview_row_activated_cb(self, widget, path, col):
263
# TODO: more than one parent
264
"""Callback for when a treeview row gets activated."""
265
revision = self.model[path][0]
266
if len(self.parent_ids[revision]) == 0:
267
# Ignore revisions without parent
269
parent_id = self.parent_ids[revision][0]
270
self.show_diff(self.branch, revision.revision_id, parent_id)
271
self.treeview.grab_focus()