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
24
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):
27
25
This object represents and manages a single window containing information
28
26
for a particular branch.
31
def __init__(self, branch, start_revs, maxnum, parent=None):
32
"""Create a new BranchWindow.
34
:param branch: Branch object for branch to show.
35
:param start_revs: Revision ids of top revisions.
36
:param maxnum: Maximum number of revisions to display,
40
Window.__init__(self, parent=parent)
30
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
41
31
self.set_border_width(0)
44
self.start_revs = start_revs
46
self.config = GlobalConfig()
48
if self.config.get_user_option('viz-compact-view') == 'yes':
49
self.compact_view = True
51
self.compact_view = False
53
self.set_title(branch._get_nick(local=True) + " - revision history")
55
# user-configured window size
56
size = self._load_size('viz-window-size')
60
# Use three-quarters of the screen by default
61
screen = self.get_screen()
62
monitor = screen.get_monitor_geometry(0)
63
width = int(monitor.width * 0.75)
64
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)
65
39
self.set_default_size(width, height)
66
self.set_size_request(width/3, height/3)
67
self._save_size_on_destroy(self, 'viz-window-size')
70
42
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
71
43
self.set_icon(icon)
73
gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
74
gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
75
gtk.accel_map_add_entry("<viz>/View/Refresh", gtk.keysyms.F5, 0)
77
45
self.accel_group = gtk.AccelGroup()
78
46
self.add_accel_group(self.accel_group)
80
if getattr(gtk.Action, 'set_tool_item_type', None) is not None:
81
# Not available before PyGtk-2.10
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()
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
width, height = widget.allocation.width, widget.allocation.height
109
value = '%sx%s' % (width, height)
110
self.config.set_user_option(config_name, value)
111
self.connect("destroy", save_size)
113
def set_revision(self, revid):
114
self.treeview.set_revision_id(revid)
116
50
def construct(self):
117
51
"""Construct the window contents."""
118
52
vbox = gtk.VBox(spacing=0)
121
self.paned = gtk.VPaned()
122
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
123
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
126
nav = self.construct_navigation()
127
menubar = self.construct_menubar()
128
vbox.pack_start(menubar, expand=False, fill=True)
129
vbox.pack_start(nav, expand=False, fill=True)
131
vbox.pack_start(self.paned, expand=True, fill=True)
132
vbox.set_focus_child(self.paned)
134
self.treeview.connect('revision-selected',
135
self._treeselection_changed_cb)
136
self.treeview.connect('revision-activated',
137
self._tree_revision_activated)
139
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)
142
def construct_menubar(self):
143
menubar = gtk.MenuBar()
145
file_menu = gtk.Menu()
146
file_menuitem = gtk.MenuItem("_File")
147
file_menuitem.set_submenu(file_menu)
149
file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
150
file_menu_close.connect('activate', lambda x: self.destroy())
152
file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
153
file_menu_quit.connect('activate', lambda x: gtk.main_quit())
155
if self._parent is not None:
156
file_menu.add(file_menu_close)
157
file_menu.add(file_menu_quit)
159
edit_menu = gtk.Menu()
160
edit_menuitem = gtk.MenuItem("_Edit")
161
edit_menuitem.set_submenu(edit_menu)
163
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
164
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
166
edit_menu_globopts = gtk.MenuItem("Global Settings")
167
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
169
edit_menu.add(edit_menu_branchopts)
170
edit_menu.add(edit_menu_globopts)
172
view_menu = gtk.Menu()
173
view_menuitem = gtk.MenuItem("_View")
174
view_menuitem.set_submenu(view_menu)
176
view_menu_refresh = self.refresh_action.create_menu_item()
177
view_menu_refresh.connect('activate', self._refresh_clicked)
179
view_menu.add(view_menu_refresh)
180
view_menu.add(gtk.SeparatorMenuItem())
182
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
183
view_menu_toolbar.set_active(True)
184
if self.config.get_user_option('viz-toolbar-visible') == 'False':
185
view_menu_toolbar.set_active(False)
187
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
189
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
190
view_menu_compact.set_active(self.compact_view)
191
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
193
view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
194
view_menu_diffs.set_active(False)
195
if self.config.get_user_option('viz-show-diffs') == 'True':
196
view_menu_diffs.set_active(True)
197
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
199
view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
200
view_menu_wide_diffs.set_active(False)
201
if self.config.get_user_option('viz-wide-diffs') == 'True':
202
view_menu_wide_diffs.set_active(True)
203
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
205
view_menu_wrap_diffs = gtk.CheckMenuItem("Wrap _Long Lines in Diffs")
206
view_menu_wrap_diffs.set_active(False)
207
if self.config.get_user_option('viz-wrap-diffs') == 'True':
208
view_menu_wrap_diffs.set_active(True)
209
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
211
view_menu.add(view_menu_toolbar)
212
view_menu.add(view_menu_compact)
213
view_menu.add(gtk.SeparatorMenuItem())
214
view_menu.add(view_menu_diffs)
215
view_menu.add(view_menu_wide_diffs)
216
view_menu.add(view_menu_wrap_diffs)
217
view_menu.add(gtk.SeparatorMenuItem())
219
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
220
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
222
# Revision numbers are pointless if there are multiple branches
223
if len(self.start_revs) > 1:
224
self.mnu_show_revno_column.set_sensitive(False)
225
self.treeview.set_property('revno-column-visible', False)
227
for (col, name) in [(self.mnu_show_revno_column, "revno"),
228
(self.mnu_show_date_column, "date")]:
229
col.set_active(self.treeview.get_property(name + "-column-visible"))
230
col.connect('toggled', self._col_visibility_changed, name)
234
go_menu.set_accel_group(self.accel_group)
235
go_menuitem = gtk.MenuItem("_Go")
236
go_menuitem.set_submenu(go_menu)
238
go_menu_next = self.next_rev_action.create_menu_item()
239
go_menu_prev = self.prev_rev_action.create_menu_item()
241
tag_image = gtk.Image()
242
tag_image.set_from_file(icon_path("tag-16.png"))
243
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
244
self.go_menu_tags.set_image(tag_image)
245
self.treeview.connect('refreshed', lambda w: self._update_tags())
247
go_menu.add(go_menu_next)
248
go_menu.add(go_menu_prev)
249
go_menu.add(gtk.SeparatorMenuItem())
250
go_menu.add(self.go_menu_tags)
252
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
253
revision_menuitem = gtk.MenuItem("_Revision")
254
revision_menuitem.set_submenu(self.revision_menu)
256
branch_menu = gtk.Menu()
257
branch_menuitem = gtk.MenuItem("_Branch")
258
branch_menuitem.set_submenu(branch_menu)
260
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
261
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
264
from bzrlib.plugins import search
266
mutter("Didn't find search plugin")
268
branch_menu.add(gtk.SeparatorMenuItem())
270
branch_index_menuitem = gtk.MenuItem("_Index")
271
branch_index_menuitem.connect('activate', self._branch_index_cb)
272
branch_menu.add(branch_index_menuitem)
274
branch_search_menuitem = gtk.MenuItem("_Search")
275
branch_search_menuitem.connect('activate', self._branch_search_cb)
276
branch_menu.add(branch_search_menuitem)
278
help_menu = gtk.Menu()
279
help_menuitem = gtk.MenuItem("_Help")
280
help_menuitem.set_submenu(help_menu)
282
help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
283
help_about_menuitem.connect('activate', self._about_dialog_cb)
285
help_menu.add(help_about_menuitem)
287
menubar.add(file_menuitem)
288
menubar.add(edit_menuitem)
289
menubar.add(view_menuitem)
290
menubar.add(go_menuitem)
291
menubar.add(revision_menuitem)
292
menubar.add(branch_menuitem)
293
menubar.add(help_menuitem)
298
66
def construct_top(self):
299
67
"""Construct the top-half of the window."""
300
# FIXME: Make broken_line_length configurable
302
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
304
for col in ["revno", "date"]:
305
option = self.config.get_user_option(col + '-column-visible')
306
if option is not None:
307
self.treeview.set_property(col + '-column-visible', option == 'True')
309
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)
311
79
self.treeview.show()
313
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
314
align.set_padding(5, 0, 0, 0)
315
align.add(self.treeview)
316
# user-configured size
317
size = self._load_size('viz-graph-size')
320
align.set_size_request(width, height)
322
(width, height) = self.get_size()
323
align.set_size_request(width, int(height / 2.5))
324
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)
329
118
def construct_navigation(self):
330
119
"""Construct the navigation buttons."""
331
self.toolbar = gtk.Toolbar()
332
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
334
self.prev_button = self.prev_rev_action.create_tool_item()
335
self.toolbar.insert(self.prev_button, -1)
337
self.next_button = self.next_rev_action.create_tool_item()
338
self.toolbar.insert(self.next_button, -1)
340
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
342
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
343
refresh_button.connect('clicked', self._refresh_clicked)
344
self.toolbar.insert(refresh_button, -1)
346
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()
350
146
def construct_bottom(self):
351
147
"""Construct the bottom half of the window."""
352
if self.config.get_user_option('viz-wide-diffs') == 'True':
353
self.diff_paned = gtk.VPaned()
355
self.diff_paned = gtk.HPaned()
148
from bzrlib.plugins.gtk.logview import LogView
149
self.logview = LogView()
356
150
(width, height) = self.get_size()
357
self.diff_paned.set_size_request(20, 20) # shrinkable
359
from bzrlib.plugins.gtk.revisionview import RevisionView
360
self.revisionview = RevisionView(branch=self.branch)
361
self.revisionview.set_size_request(width/3, int(height / 2.5))
362
# user-configured size
363
size = self._load_size('viz-revisionview-size')
366
self.revisionview.set_size_request(width, height)
367
self._save_size_on_destroy(self.revisionview, 'viz-revisionview-size')
368
self.revisionview.show()
369
self.revisionview.set_show_callback(self._show_clicked_cb)
370
self.revisionview.connect('notify::revision', self._go_clicked_cb)
371
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
372
self.diff_paned.pack1(self.revisionview)
374
from bzrlib.plugins.gtk.diff import DiffWidget
375
self.diff = DiffWidget()
376
self.diff_paned.pack2(self.diff)
378
self.diff_paned.show_all()
379
if self.config.get_user_option('viz-show-diffs') != 'True':
382
return self.diff_paned
384
def _tag_selected_cb(self, menuitem, revid):
385
self.treeview.set_revision_id(revid)
387
def _treeselection_changed_cb(self, selection, *args):
388
"""callback for when the treeview changes."""
389
revision = self.treeview.get_revision()
390
parents = self.treeview.get_parents()
391
children = self.treeview.get_children()
393
self.revision_menu.set_revision_ids([revision.revision_id])
395
if revision and revision != NULL_REVISION:
396
prev_menu = gtk.Menu()
398
self.prev_rev_action.set_sensitive(True)
399
for parent_id in parents:
400
if parent_id and parent_id != NULL_REVISION:
401
parent = self.branch.repository.get_revision(parent_id)
403
str = ' (' + parent.properties['branch-nick'] + ')'
407
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
408
item.connect('activate', self._set_revision_cb, parent_id)
412
self.prev_rev_action.set_sensitive(False)
415
if getattr(self.prev_button, 'set_menu', None) is not None:
416
self.prev_button.set_menu(prev_menu)
418
next_menu = gtk.Menu()
419
if len(children) > 0:
420
self.next_rev_action.set_sensitive(True)
421
for child_id in children:
422
child = self.branch.repository.get_revision(child_id)
424
str = ' (' + child.properties['branch-nick'] + ')'
428
item = gtk.MenuItem(child.message.split("\n")[0] + str)
429
item.connect('activate', self._set_revision_cb, child_id)
433
self.next_rev_action.set_sensitive(False)
436
if getattr(self.next_button, 'set_menu', None) is not None:
437
self.next_button.set_menu(next_menu)
439
self.revisionview.set_revision(revision)
440
self.revisionview.set_children(children)
441
self.update_diff_panel(revision, parents)
443
def _tree_revision_activated(self, widget, path, col):
444
# TODO: more than one parent
445
"""Callback for when a treeview row gets activated."""
446
revision = self.treeview.get_revision()
447
parents = self.treeview.get_parents()
449
if len(parents) == 0:
450
parent_id = NULL_REVISION
452
parent_id = parents[0]
454
self.show_diff(revision.revision_id, parent_id)
455
self.treeview.grab_focus()
151
self.logview.set_size_request(width, int(height / 2.5))
153
self.logview.set_show_callback(self._show_clicked_cb)
154
self.logview.set_go_callback(self._go_clicked_cb)
157
def set_branch(self, branch, start, maxnum):
158
"""Set the branch and start position for this window.
160
Creates a new TreeModel and populates it with information about
161
the new branch before updating the window title and model of the
166
# [ revision, node, last_lines, lines, message, committer, timestamp ]
167
self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
168
gobject.TYPE_PYOBJECT,
169
gobject.TYPE_PYOBJECT,
170
gobject.TYPE_PYOBJECT,
176
(self.revisions, colours, self.children, self.parent_ids,
177
merge_sorted) = distances(branch, start)
178
for (index, (revision, node, lines)) in enumerate(graph(
179
self.revisions, colours, merge_sorted)):
180
# FIXME: at this point we should be able to show the graph order
181
# and lines with no message or commit data - and then incrementally
182
# fill the timestamp, committer etc data as desired.
183
message = revision.message.split("\n")[0]
184
if revision.committer is not None:
185
timestamp = format_date(revision.timestamp, revision.timezone)
188
self.model.append([revision, node, last_lines, lines,
189
message, revision.committer, timestamp])
190
self.index[revision] = index
192
if maxnum is not None and index > maxnum:
195
self.set_title(branch.nick + " - bzrk")
196
self.treeview.set_model(self.model)
198
def _treeview_cursor_cb(self, *args):
199
"""Callback for when the treeview cursor changes."""
200
(path, col) = self.treeview.get_cursor()
201
revision = self.model[path][0]
203
self.back_button.set_sensitive(len(self.parent_ids[revision]) > 0)
204
self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
205
self.logview.set_revision(revision)
457
207
def _back_clicked_cb(self, *args):
458
208
"""Callback for when the back button is clicked."""
209
(path, col) = self.treeview.get_cursor()
210
revision = self.model[path][0]
211
if not len(self.parent_ids[revision]):
214
for parent_id in self.parent_ids[revision]:
215
parent = self.revisions[parent_id]
216
if same_branch(revision, parent):
217
self.treeview.set_cursor(self.index[parent])
220
next = self.revisions[self.parent_ids[revision][0]]
221
self.treeview.set_cursor(self.index[next])
222
self.treeview.grab_focus()
461
224
def _fwd_clicked_cb(self, *args):
462
225
"""Callback for when the forward button is clicked."""
463
self.treeview.forward()
465
def _go_clicked_cb(self, w, p):
226
(path, col) = self.treeview.get_cursor()
227
revision = self.model[path][0]
228
if not len(self.children[revision]):
231
for child in self.children[revision]:
232
if same_branch(child, revision):
233
self.treeview.set_cursor(self.index[child])
236
prev = list(self.children[revision])[0]
237
self.treeview.set_cursor(self.index[prev])
238
self.treeview.grab_focus()
240
def _go_clicked_cb(self, revid):
466
241
"""Callback for when the go button for a parent is clicked."""
467
if self.revisionview.get_revision() is not None:
468
self.treeview.set_revision(self.revisionview.get_revision())
470
def _show_clicked_cb(self, revid, parentid):
471
"""Callback for when the show button for a parent is clicked."""
472
self.show_diff(revid, parentid)
242
self.treeview.set_cursor(self.index[self.revisions[revid]])
473
243
self.treeview.grab_focus()
475
def _set_revision_cb(self, w, revision_id):
476
self.treeview.set_revision_id(revision_id)
478
def _brokenlines_toggled_cb(self, button):
479
self.compact_view = button.get_active()
481
if self.compact_view:
486
self.config.set_user_option('viz-compact-view', option)
487
self.treeview.set_property('compact', self.compact_view)
488
self.treeview.refresh()
490
def _branch_index_cb(self, w):
491
from bzrlib.plugins.search import index as _mod_index
492
_mod_index.index_url(self.branch.base)
494
def _branch_search_cb(self, w):
495
from bzrlib.plugins.search import index as _mod_index
496
from bzrlib.plugins.gtk.search import SearchDialog
497
from bzrlib.plugins.search import errors as search_errors
500
index = _mod_index.open_index_url(self.branch.base)
501
except search_errors.NoSearchIndex:
502
dialog = gtk.MessageDialog(self, type=gtk.MESSAGE_QUESTION,
503
buttons=gtk.BUTTONS_OK_CANCEL,
504
message_format="This branch has not been indexed yet. "
506
if dialog.run() == gtk.RESPONSE_OK:
508
index = _mod_index.index_url(self.branch.base)
513
dialog = SearchDialog(index)
515
if dialog.run() == gtk.RESPONSE_OK:
516
self.set_revision(dialog.get_revision())
520
def _about_dialog_cb(self, w):
521
from bzrlib.plugins.gtk.about import AboutDialog
524
def _col_visibility_changed(self, col, property):
525
self.config.set_user_option(property + '-column-visible', col.get_active())
526
self.treeview.set_property(property + '-column-visible', col.get_active())
528
def _toolbar_visibility_changed(self, col):
533
self.config.set_user_option('viz-toolbar-visible', col.get_active())
535
def _make_diff_nonzero_size(self):
536
"""make sure the diff isn't zero-width or zero-height"""
537
alloc = self.diff.get_allocation()
538
if (alloc.width < 10) or (alloc.height < 10):
539
width, height = self.get_size()
540
self.revisionview.set_size_request(width/3, int(height / 2.5))
542
def _diff_visibility_changed(self, col):
543
"""Hide or show the diff panel."""
546
self._make_diff_nonzero_size()
549
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
550
self.update_diff_panel()
552
def _diff_placement_changed(self, col):
553
"""Toggle the diff panel's position."""
554
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
556
old = self.paned.get_child2()
557
self.paned.remove(old)
558
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
559
self._make_diff_nonzero_size()
561
self.treeview.emit('revision-selected')
563
def _diff_wrap_changed(self, widget):
564
"""Toggle word wrap in the diff widget."""
565
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
566
self.diff._on_wraplines_toggled(widget)
568
def _refresh_clicked(self, w):
569
self.treeview.refresh()
571
def _update_tags(self):
574
if self.branch.supports_tags():
575
tags = self.branch.tags.get_tag_dict().items()
576
tags.sort(reverse=True)
577
for tag, revid in tags:
578
tag_image = gtk.Image()
579
tag_image.set_from_file(icon_path('tag-16.png'))
580
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
581
tag_item.set_image(tag_image)
582
tag_item.connect('activate', self._tag_selected_cb, revid)
583
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
585
self.go_menu_tags.set_submenu(menu)
587
self.go_menu_tags.set_sensitive(len(tags) != 0)
589
self.go_menu_tags.set_sensitive(False)
591
self.go_menu_tags.show_all()
593
def _load_size(self, name):
594
"""Read and parse 'name' from self.config.
595
The value is a string, formatted as WIDTHxHEIGHT
596
Returns None, or (width, height)
598
size = self.config.get_user_option(name)
600
width, height = [int(num) for num in size.split('x')]
601
# avoid writing config every time we start
605
def show_diff(self, revid=None, parentid=NULL_REVISION):
245
def show_diff(self, branch, revid, parentid):
606
246
"""Open a new window to show a diff between the given revisions."""
607
247
from bzrlib.plugins.gtk.diff import DiffWindow
608
window = DiffWindow(parent=self)
610
rev_tree = self.branch.repository.revision_tree(revid)
611
parent_tree = self.branch.repository.revision_tree(parentid)
613
description = revid + " - " + self.branch._get_nick(local=True)
248
window = DiffWindow()
249
rev_tree = branch.repository.revision_tree(revid)
250
parent_tree = branch.repository.revision_tree(parentid)
251
description = revid + " - " + branch.nick
614
252
window.set_diff(description, rev_tree, parent_tree)
617
def update_diff_panel(self, revision=None, parents=None):
618
"""Show the current revision in the diff panel."""
619
if self.config.get_user_option('viz-show-diffs') != 'True':
622
if not revision: # default to selected row
623
revision = self.treeview.get_revision()
624
if revision == NULL_REVISION:
627
if not parents: # default to selected row's parents
628
parents = self.treeview.get_parents()
629
if len(parents) == 0:
630
parent_id = NULL_REVISION
632
parent_id = parents[0]
634
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
635
parent_tree = self.branch.repository.revision_tree(parent_id)
637
self.diff.set_diff(rev_tree, parent_tree)
638
if self.config.get_user_option('viz-wrap-diffs') == 'True':
639
self.diff._on_wraplines_toggled(wrap=True)
255
def _show_clicked_cb(self, revid, parentid):
256
"""Callback for when the show button for a parent is clicked."""
257
self.show_diff(self.branch, revid, parentid)
258
self.treeview.grab_focus()
260
def _treeview_row_activated_cb(self, widget, path, col):
261
# TODO: more than one parent
262
"""Callback for when a treeview row gets activated."""
263
revision = self.model[path][0]
264
if len(self.parent_ids[revision]) == 0:
265
# Ignore revisions without parent
267
parent_id = self.parent_ids[revision][0]
268
self.show_diff(self.branch, revision.revision_id, parent_id)
269
self.treeview.grab_focus()