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
self.go_menu_tags = gtk.MenuItem("_Tags")
176
go_menu.add(go_menu_next)
177
go_menu.add(go_menu_prev)
178
go_menu.add(gtk.SeparatorMenuItem())
179
go_menu.add(self.go_menu_tags)
181
revision_menu = gtk.Menu()
182
revision_menuitem = gtk.MenuItem("_Revision")
183
revision_menuitem.set_submenu(revision_menu)
185
revision_menu_diff = gtk.MenuItem("View Changes")
186
revision_menu_diff.connect('activate',
187
lambda w: self.treeview.show_diff())
189
revision_menu_tag = gtk.MenuItem("Tag Revision")
190
revision_menu_tag.connect('activate', self._tag_revision_cb)
192
revision_menu.add(revision_menu_tag)
193
revision_menu.add(revision_menu_diff)
195
branch_menu = gtk.Menu()
196
branch_menuitem = gtk.MenuItem("_Branch")
197
branch_menuitem.set_submenu(branch_menu)
199
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
200
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
202
help_menu = gtk.Menu()
203
help_menuitem = gtk.MenuItem("_Help")
204
help_menuitem.set_submenu(help_menu)
206
help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
207
help_about_menuitem.connect('activate', self._about_dialog_cb)
209
help_menu.add(help_about_menuitem)
211
menubar.add(file_menuitem)
212
menubar.add(edit_menuitem)
213
menubar.add(view_menuitem)
214
menubar.add(go_menuitem)
215
menubar.add(revision_menuitem)
216
menubar.add(branch_menuitem)
217
menubar.add(help_menuitem)
222
def construct_loading_msg(self):
223
image_loading = gtk.image_new_from_stock(gtk.STOCK_REFRESH,
224
gtk.ICON_SIZE_BUTTON)
227
label_loading = gtk.Label(_("Please wait, loading ancestral graph..."))
228
label_loading.set_alignment(0.0, 0.5)
231
self.loading_msg_box = gtk.HBox()
232
self.loading_msg_box.set_spacing(5)
233
self.loading_msg_box.set_border_width(5)
234
self.loading_msg_box.pack_start(image_loading, False, False)
235
self.loading_msg_box.pack_start(label_loading, True, True)
236
self.loading_msg_box.show()
238
return self.loading_msg_box
240
def construct_top(self):
241
"""Construct the top-half of the window."""
242
# FIXME: Make broken_line_length configurable
244
self.treeview = TreeView(self.branch, self.start, self.maxnum, self.compact_view)
246
self.treeview.connect('revision-selected',
247
self._treeselection_changed_cb)
249
self.treeview.connect('revisions-loaded',
250
lambda x: self.loading_msg_box.hide())
252
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
254
for col in ["revno", "date"]:
255
option = self.config.get_user_option(col + '-column-visible')
256
if option is not None:
257
self.treeview.set_property(col + '-column-visible', option == 'True')
261
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
262
align.set_padding(5, 0, 0, 0)
263
align.add(self.treeview)
268
def construct_navigation(self):
269
"""Construct the navigation buttons."""
270
self.toolbar = gtk.Toolbar()
271
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
273
self.prev_button = self.prev_rev_action.create_tool_item()
274
self.toolbar.insert(self.prev_button, -1)
276
self.next_button = self.next_rev_action.create_tool_item()
277
self.toolbar.insert(self.next_button, -1)
279
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
281
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
282
refresh_button.connect('clicked', self._refresh_clicked)
283
self.toolbar.insert(refresh_button, -1)
285
self.toolbar.show_all()
289
def construct_bottom(self):
290
"""Construct the bottom half of the window."""
291
from bzrlib.plugins.gtk.revisionview import RevisionView
292
self.revisionview = RevisionView(branch=self.branch)
293
(width, height) = self.get_size()
294
self.revisionview.set_size_request(width, int(height / 2.5))
295
self.revisionview.show()
296
self.revisionview.set_show_callback(self._show_clicked_cb)
297
self.revisionview.connect('notify::revision', self._go_clicked_cb)
298
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
299
return self.revisionview
301
def _tag_selected_cb(self, menuitem, revid):
302
self.treeview.set_revision_id(revid)
304
def _treeselection_changed_cb(self, selection, *args):
305
"""callback for when the treeview changes."""
306
revision = self.treeview.get_revision()
307
parents = self.treeview.get_parents()
308
children = self.treeview.get_children()
310
if revision is not None:
311
prev_menu = gtk.Menu()
313
self.prev_rev_action.set_sensitive(True)
314
for parent_id in parents:
315
parent = self.branch.repository.get_revision(parent_id)
317
str = ' (' + parent.properties['branch-nick'] + ')'
321
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
322
item.connect('activate', self._set_revision_cb, parent_id)
326
self.prev_rev_action.set_sensitive(False)
329
self.prev_button.set_menu(prev_menu)
331
next_menu = gtk.Menu()
332
if len(children) > 0:
333
self.next_rev_action.set_sensitive(True)
334
for child_id in children:
335
child = self.branch.repository.get_revision(child_id)
337
str = ' (' + child.properties['branch-nick'] + ')'
341
item = gtk.MenuItem(child.message.split("\n")[0] + str)
342
item.connect('activate', self._set_revision_cb, child_id)
346
self.next_rev_action.set_sensitive(False)
349
self.next_button.set_menu(next_menu)
351
self.revisionview.set_revision(revision)
352
self.revisionview.set_children(children)
354
def _back_clicked_cb(self, *args):
355
"""Callback for when the back button is clicked."""
358
def _fwd_clicked_cb(self, *args):
359
"""Callback for when the forward button is clicked."""
360
self.treeview.forward()
362
def _go_clicked_cb(self, w, p):
363
"""Callback for when the go button for a parent is clicked."""
364
if self.revisionview.get_revision() is not None:
365
self.treeview.set_revision(self.revisionview.get_revision())
367
def _show_clicked_cb(self, revid, parentid):
368
"""Callback for when the show button for a parent is clicked."""
369
self.treeview.show_diff(revid, parentid)
370
self.treeview.grab_focus()
372
def _set_revision_cb(self, w, revision_id):
373
self.treeview.set_revision_id(revision_id)
375
def _brokenlines_toggled_cb(self, button):
376
self.compact_view = button.get_active()
378
if self.compact_view:
383
self.config.set_user_option('viz-compact-view', option)
384
self.treeview.set_property('compact', self.compact_view)
385
self.treeview.refresh()
387
def _tag_revision_cb(self, w):
389
self.treeview.set_sensitive(False)
390
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
391
response = dialog.run()
392
if response != gtk.RESPONSE_NONE:
395
if response == gtk.RESPONSE_OK:
396
self.treeview.add_tag(dialog.tagname, dialog._revid)
401
self.treeview.set_sensitive(True)
403
def _about_dialog_cb(self, w):
404
from bzrlib.plugins.gtk.about import AboutDialog
408
def _col_visibility_changed(self, col, property):
409
self.config.set_user_option(property + '-column-visible', col.get_active())
410
self.treeview.set_property(property + '-column-visible', col.get_active())
412
def _toolbar_visibility_changed(self, col):
418
def _show_about_cb(self, w):
419
dialog = AboutDialog()
420
dialog.connect('response', lambda d,r: d.destroy())
423
def _refresh_clicked(self, w):
424
self.treeview.update()
426
def _update_tags(self):
429
if self.branch.supports_tags():
430
tags = self.branch.tags.get_tag_dict().items()
433
for tag, revid in tags:
434
tag_item = gtk.MenuItem(tag)
435
tag_item.connect('activate', self._tag_selected_cb, revid)
437
self.go_menu_tags.set_submenu(menu)
439
self.go_menu_tags.set_sensitive(len(tags) != 0)
441
self.go_menu_tags.set_sensitive(False)
443
self.go_menu_tags.show_all()