1
# -*- coding: UTF-8 -*-
4
This module contains the code to manage the branch information window,
5
which contains both the revision graph and details panes.
8
__copyright__ = "Copyright © 2005 Canonical Ltd."
9
__author__ = "Scott James Remnant <scott@ubuntu.com>"
16
from bzrlib.plugins.gtk.window import Window
17
from bzrlib.plugins.gtk.tags import AddTagDialog
18
from bzrlib.plugins.gtk.preferences import PreferencesWindow
19
from bzrlib.plugins.gtk.branchview import TreeView
20
from bzrlib.revision import Revision
21
from bzrlib.config import BranchConfig
22
from bzrlib.config import GlobalConfig
24
class BranchWindow(Window):
27
This object represents and manages a single window containing information
28
for a particular branch.
31
def __init__(self, branch, start, maxnum, parent=None):
32
"""Create a new BranchWindow.
34
:param branch: Branch object for branch to show.
35
:param start: Revision id of top revision.
36
:param maxnum: Maximum number of revisions to display,
40
Window.__init__(self, parent=parent)
41
self.set_border_width(0)
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.nick + " - revision history")
55
# Use three-quarters of the screen by default
56
screen = self.get_screen()
57
monitor = screen.get_monitor_geometry(0)
58
width = int(monitor.width * 0.75)
59
height = int(monitor.height * 0.75)
60
self.set_default_size(width, height)
63
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
66
gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
67
gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
69
self.accel_group = gtk.AccelGroup()
70
self.add_accel_group(self.accel_group)
72
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
74
self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
75
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
76
self.prev_rev_action.set_accel_group(self.accel_group)
77
self.prev_rev_action.connect("activate", self._back_clicked_cb)
78
self.prev_rev_action.connect_accelerator()
80
self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
81
self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
82
self.next_rev_action.set_accel_group(self.accel_group)
83
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
84
self.next_rev_action.connect_accelerator()
88
def set_revision(self, revid):
89
self.treeview.set_revision_id(revid)
92
"""Construct the window contents."""
93
vbox = gtk.VBox(spacing=0)
96
self.paned = gtk.VPaned()
97
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
98
self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
101
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
102
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
103
vbox.pack_start(self.construct_loading_msg(), expand=False, fill=True)
105
vbox.pack_start(self.paned, expand=True, fill=True)
106
vbox.set_focus_child(self.paned)
110
def construct_menubar(self):
111
menubar = gtk.MenuBar()
113
file_menu = gtk.Menu()
114
file_menuitem = gtk.MenuItem("_File")
115
file_menuitem.set_submenu(file_menu)
117
file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
118
file_menu_close.connect('activate', lambda x: self.destroy())
120
file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
121
file_menu_quit.connect('activate', lambda x: gtk.main_quit())
123
if self._parent is not None:
124
file_menu.add(file_menu_close)
125
file_menu.add(file_menu_quit)
127
edit_menu = gtk.Menu()
128
edit_menuitem = gtk.MenuItem("_Edit")
129
edit_menuitem.set_submenu(edit_menu)
131
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
133
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
134
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
136
edit_menu_globopts = gtk.MenuItem("Global Settings")
137
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
139
edit_menu.add(edit_menu_find)
140
edit_menu.add(edit_menu_branchopts)
141
edit_menu.add(edit_menu_globopts)
143
view_menu = gtk.Menu()
144
view_menuitem = gtk.MenuItem("_View")
145
view_menuitem.set_submenu(view_menu)
147
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
148
view_menu_toolbar.set_active(True)
149
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
151
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
152
view_menu_compact.set_active(self.compact_view)
153
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
155
view_menu.add(view_menu_toolbar)
156
view_menu.add(view_menu_compact)
157
view_menu.add(gtk.SeparatorMenuItem())
159
for (label, name) in [("Revision _Number", "revno"), ("_Date", "date")]:
160
col = gtk.CheckMenuItem("Show " + label + " Column")
161
col.set_active(self.treeview.get_property(name + "-column-visible"))
162
col.connect('toggled', self._col_visibility_changed, name)
166
go_menu.set_accel_group(self.accel_group)
167
go_menuitem = gtk.MenuItem("_Go")
168
go_menuitem.set_submenu(go_menu)
170
go_menu_next = self.next_rev_action.create_menu_item()
171
go_menu_prev = self.prev_rev_action.create_menu_item()
173
tags_menu = gtk.Menu()
174
go_menu_tags = gtk.MenuItem("_Tags")
175
go_menu_tags.set_submenu(tags_menu)
177
if self.branch.supports_tags():
178
tags = self.branch.tags.get_tag_dict().items()
181
for tag, revid in tags:
182
tag_item = gtk.MenuItem(tag)
183
tag_item.connect('activate', self._tag_selected_cb, revid)
184
tags_menu.add(tag_item)
186
go_menu_tags.set_sensitive(len(tags) != 0)
188
go_menu_tags.set_sensitive(False)
190
go_menu.add(go_menu_next)
191
go_menu.add(go_menu_prev)
192
go_menu.add(gtk.SeparatorMenuItem())
193
go_menu.add(go_menu_tags)
195
revision_menu = gtk.Menu()
196
revision_menuitem = gtk.MenuItem("_Revision")
197
revision_menuitem.set_submenu(revision_menu)
199
revision_menu_diff = gtk.MenuItem("View Changes")
200
revision_menu_diff.connect('activate',
201
lambda w: self.treeview.show_diff())
203
revision_menu_tag = gtk.MenuItem("Tag Revision")
204
revision_menu_tag.connect('activate', self._tag_revision_cb)
206
revision_menu.add(revision_menu_tag)
207
revision_menu.add(revision_menu_diff)
209
branch_menu = gtk.Menu()
210
branch_menuitem = gtk.MenuItem("_Branch")
211
branch_menuitem.set_submenu(branch_menu)
213
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
214
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
216
menubar.add(file_menuitem)
217
menubar.add(edit_menuitem)
218
menubar.add(view_menuitem)
219
menubar.add(go_menuitem)
220
menubar.add(revision_menuitem)
221
menubar.add(branch_menuitem)
226
def construct_loading_msg(self):
227
image_loading = gtk.image_new_from_stock(gtk.STOCK_REFRESH,
228
gtk.ICON_SIZE_BUTTON)
231
label_loading = gtk.Label(_("Please wait, loading ancestral graph..."))
232
label_loading.set_alignment(0.0, 0.5)
235
self.loading_msg_box = gtk.HBox()
236
self.loading_msg_box.set_spacing(5)
237
self.loading_msg_box.set_border_width(5)
238
self.loading_msg_box.pack_start(image_loading, False, False)
239
self.loading_msg_box.pack_start(label_loading, True, True)
240
self.loading_msg_box.show()
242
return self.loading_msg_box
244
def construct_top(self):
245
"""Construct the top-half of the window."""
246
# FIXME: Make broken_line_length configurable
248
self.treeview = TreeView(self.branch, self.start, self.maxnum, self.compact_view)
250
self.treeview.connect('revision-selected',
251
self._treeselection_changed_cb)
253
self.treeview.connect('revisions-loaded',
254
lambda x: self.loading_msg_box.hide())
256
for col in ["revno", "date"]:
257
option = self.config.get_user_option(col + '-column-visible')
258
if option is not None:
259
self.treeview.set_property(col + '-column-visible', option == 'True')
263
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
264
align.set_padding(5, 0, 0, 0)
265
align.add(self.treeview)
270
def construct_navigation(self):
271
"""Construct the navigation buttons."""
272
self.toolbar = gtk.Toolbar()
273
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
275
self.prev_button = self.prev_rev_action.create_tool_item()
276
self.toolbar.insert(self.prev_button, -1)
278
self.next_button = self.next_rev_action.create_tool_item()
279
self.toolbar.insert(self.next_button, -1)
281
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
283
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
284
refresh_button.connect('clicked', self._refresh_clicked)
285
self.toolbar.insert(refresh_button, -1)
287
self.toolbar.show_all()
291
def construct_bottom(self):
292
"""Construct the bottom half of the window."""
293
from bzrlib.plugins.gtk.revisionview import RevisionView
294
self.revisionview = RevisionView(branch=self.branch)
295
(width, height) = self.get_size()
296
self.revisionview.set_size_request(width, int(height / 2.5))
297
self.revisionview.show()
298
self.revisionview.set_show_callback(self._show_clicked_cb)
299
self.revisionview.connect('notify::revision', self._go_clicked_cb)
300
return self.revisionview
302
def _tag_selected_cb(self, menuitem, revid):
303
self.treeview.set_revision_id(revid)
305
def _treeselection_changed_cb(self, selection, *args):
306
"""callback for when the treeview changes."""
307
revision = self.treeview.get_revision()
308
parents = self.treeview.get_parents()
309
children = self.treeview.get_children()
311
if revision is not None:
312
prev_menu = gtk.Menu()
314
self.prev_rev_action.set_sensitive(True)
315
for parent_id in parents:
316
parent = self.branch.repository.get_revision(parent_id)
318
str = ' (' + parent.properties['branch-nick'] + ')'
322
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
323
item.connect('activate', self._set_revision_cb, parent_id)
327
self.prev_rev_action.set_sensitive(False)
330
self.prev_button.set_menu(prev_menu)
332
next_menu = gtk.Menu()
333
if len(children) > 0:
334
self.next_rev_action.set_sensitive(True)
335
for child_id in children:
336
child = self.branch.repository.get_revision(child_id)
338
str = ' (' + child.properties['branch-nick'] + ')'
342
item = gtk.MenuItem(child.message.split("\n")[0] + str)
343
item.connect('activate', self._set_revision_cb, child_id)
347
self.next_rev_action.set_sensitive(False)
350
self.next_button.set_menu(next_menu)
352
self.revisionview.set_revision(revision)
353
self.revisionview.set_children(children)
355
def _back_clicked_cb(self, *args):
356
"""Callback for when the back button is clicked."""
359
def _fwd_clicked_cb(self, *args):
360
"""Callback for when the forward button is clicked."""
361
self.treeview.forward()
363
def _go_clicked_cb(self, w, p):
364
"""Callback for when the go button for a parent is clicked."""
365
if self.revisionview.get_revision() is not None:
366
self.treeview.set_revision(self.revisionview.get_revision())
368
def _show_clicked_cb(self, revid, parentid):
369
"""Callback for when the show button for a parent is clicked."""
370
self.treeview.show_diff(revid, parentid)
371
self.treeview.grab_focus()
373
def _set_revision_cb(self, w, revision_id):
374
self.treeview.set_revision_id(revision_id)
376
def _brokenlines_toggled_cb(self, button):
377
self.compact_view = button.get_active()
379
if self.compact_view:
384
self.config.set_user_option('viz-compact-view', option)
385
self.treeview.set_property('compact', self.compact_view)
386
self.treeview.refresh()
388
def _tag_revision_cb(self, w):
390
self.treeview.set_sensitive(False)
392
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
393
response = dialog.run()
394
if response != gtk.RESPONSE_NONE:
397
if response == gtk.RESPONSE_OK:
399
self.branch.lock_write()
400
self.branch.tags.set_tag(dialog.tagname, dialog._revid)
407
self.branch.lock_read()
408
self.treeview.emit("revision-selected")
409
self.treeview.set_sensitive(True)
411
def _col_visibility_changed(self, col, property):
412
self.config.set_user_option(property + '-column-visible', col.get_active())
413
self.treeview.set_property(property + '-column-visible', col.get_active())
415
def _toolbar_visibility_changed(self, col):
421
def _show_about_cb(self, w):
422
dialog = AboutDialog()
423
dialog.connect('response', lambda d,r: d.destroy())
426
def _refresh_clicked(self, w):
427
self.treeview.update()