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
menubar.add(file_menuitem)
203
menubar.add(edit_menuitem)
204
menubar.add(view_menuitem)
205
menubar.add(go_menuitem)
206
menubar.add(revision_menuitem)
207
menubar.add(branch_menuitem)
212
def construct_loading_msg(self):
213
image_loading = gtk.image_new_from_stock(gtk.STOCK_REFRESH,
214
gtk.ICON_SIZE_BUTTON)
217
label_loading = gtk.Label(_("Please wait, loading ancestral graph..."))
218
label_loading.set_alignment(0.0, 0.5)
221
self.loading_msg_box = gtk.HBox()
222
self.loading_msg_box.set_spacing(5)
223
self.loading_msg_box.set_border_width(5)
224
self.loading_msg_box.pack_start(image_loading, False, False)
225
self.loading_msg_box.pack_start(label_loading, True, True)
226
self.loading_msg_box.show()
228
return self.loading_msg_box
230
def construct_top(self):
231
"""Construct the top-half of the window."""
232
# FIXME: Make broken_line_length configurable
234
self.treeview = TreeView(self.branch, self.start, self.maxnum, self.compact_view)
236
self.treeview.connect('revision-selected',
237
self._treeselection_changed_cb)
239
self.treeview.connect('revisions-loaded',
240
lambda x: self.loading_msg_box.hide())
242
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
244
for col in ["revno", "date"]:
245
option = self.config.get_user_option(col + '-column-visible')
246
if option is not None:
247
self.treeview.set_property(col + '-column-visible', option == 'True')
251
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
252
align.set_padding(5, 0, 0, 0)
253
align.add(self.treeview)
258
def construct_navigation(self):
259
"""Construct the navigation buttons."""
260
self.toolbar = gtk.Toolbar()
261
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
263
self.prev_button = self.prev_rev_action.create_tool_item()
264
self.toolbar.insert(self.prev_button, -1)
266
self.next_button = self.next_rev_action.create_tool_item()
267
self.toolbar.insert(self.next_button, -1)
269
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
271
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
272
refresh_button.connect('clicked', self._refresh_clicked)
273
self.toolbar.insert(refresh_button, -1)
275
self.toolbar.show_all()
279
def construct_bottom(self):
280
"""Construct the bottom half of the window."""
281
from bzrlib.plugins.gtk.revisionview import RevisionView
282
self.revisionview = RevisionView(branch=self.branch)
283
(width, height) = self.get_size()
284
self.revisionview.set_size_request(width, int(height / 2.5))
285
self.revisionview.show()
286
self.revisionview.set_show_callback(self._show_clicked_cb)
287
self.revisionview.connect('notify::revision', self._go_clicked_cb)
288
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
289
return self.revisionview
291
def _tag_selected_cb(self, menuitem, revid):
292
self.treeview.set_revision_id(revid)
294
def _treeselection_changed_cb(self, selection, *args):
295
"""callback for when the treeview changes."""
296
revision = self.treeview.get_revision()
297
parents = self.treeview.get_parents()
298
children = self.treeview.get_children()
300
if revision is not None:
301
prev_menu = gtk.Menu()
303
self.prev_rev_action.set_sensitive(True)
304
for parent_id in parents:
305
parent = self.branch.repository.get_revision(parent_id)
307
str = ' (' + parent.properties['branch-nick'] + ')'
311
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
312
item.connect('activate', self._set_revision_cb, parent_id)
316
self.prev_rev_action.set_sensitive(False)
319
self.prev_button.set_menu(prev_menu)
321
next_menu = gtk.Menu()
322
if len(children) > 0:
323
self.next_rev_action.set_sensitive(True)
324
for child_id in children:
325
child = self.branch.repository.get_revision(child_id)
327
str = ' (' + child.properties['branch-nick'] + ')'
331
item = gtk.MenuItem(child.message.split("\n")[0] + str)
332
item.connect('activate', self._set_revision_cb, child_id)
336
self.next_rev_action.set_sensitive(False)
339
self.next_button.set_menu(next_menu)
341
self.revisionview.set_revision(revision)
342
self.revisionview.set_children(children)
344
def _back_clicked_cb(self, *args):
345
"""Callback for when the back button is clicked."""
348
def _fwd_clicked_cb(self, *args):
349
"""Callback for when the forward button is clicked."""
350
self.treeview.forward()
352
def _go_clicked_cb(self, w, p):
353
"""Callback for when the go button for a parent is clicked."""
354
if self.revisionview.get_revision() is not None:
355
self.treeview.set_revision(self.revisionview.get_revision())
357
def _show_clicked_cb(self, revid, parentid):
358
"""Callback for when the show button for a parent is clicked."""
359
self.treeview.show_diff(revid, parentid)
360
self.treeview.grab_focus()
362
def _set_revision_cb(self, w, revision_id):
363
self.treeview.set_revision_id(revision_id)
365
def _brokenlines_toggled_cb(self, button):
366
self.compact_view = button.get_active()
368
if self.compact_view:
373
self.config.set_user_option('viz-compact-view', option)
374
self.treeview.set_property('compact', self.compact_view)
375
self.treeview.refresh()
377
def _tag_revision_cb(self, w):
379
self.treeview.set_sensitive(False)
380
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
381
response = dialog.run()
382
if response != gtk.RESPONSE_NONE:
385
if response == gtk.RESPONSE_OK:
386
self.treeview.add_tag(dialog.tagname, dialog._revid)
391
self.treeview.set_sensitive(True)
393
def _col_visibility_changed(self, col, property):
394
self.config.set_user_option(property + '-column-visible', col.get_active())
395
self.treeview.set_property(property + '-column-visible', col.get_active())
397
def _toolbar_visibility_changed(self, col):
403
def _show_about_cb(self, w):
404
dialog = AboutDialog()
405
dialog.connect('response', lambda d,r: d.destroy())
408
def _refresh_clicked(self, w):
409
self.treeview.update()
411
def _update_tags(self):
414
if self.branch.supports_tags():
415
tags = self.branch.tags.get_tag_dict().items()
418
for tag, revid in tags:
419
tag_item = gtk.MenuItem(tag)
420
tag_item.connect('activate', self._tag_selected_cb, revid)
422
self.go_menu_tags.set_submenu(menu)
424
self.go_menu_tags.set_sensitive(len(tags) != 0)
426
self.go_menu_tags.set_sensitive(False)
428
self.go_menu_tags.show_all()