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.revision import Revision
20
from bzrlib.config import BranchConfig
21
from bzrlib.config import GlobalConfig
22
from treeview import TreeView
23
from about import AboutDialog
25
class BranchWindow(Window):
28
This object represents and manages a single window containing information
29
for a particular branch.
32
def __init__(self, branch, start, maxnum, parent=None):
33
"""Create a new BranchWindow.
35
:param branch: Branch object for branch to show.
36
:param start: Revision id of top revision.
37
:param maxnum: Maximum number of revisions to display,
41
Window.__init__(self, parent=parent)
42
self.set_border_width(0)
47
self.config = GlobalConfig()
49
if self.config.get_user_option('viz-compact-view') == 'yes':
50
self.compact_view = True
52
self.compact_view = False
54
self.set_title(branch.nick + " - revision history")
56
# Use three-quarters of the screen by default
57
screen = self.get_screen()
58
monitor = screen.get_monitor_geometry(0)
59
width = int(monitor.width * 0.75)
60
height = int(monitor.height * 0.75)
61
self.set_default_size(width, height)
64
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
67
gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
68
gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
70
self.accel_group = gtk.AccelGroup()
71
self.add_accel_group(self.accel_group)
73
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
75
self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
76
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
77
self.prev_rev_action.set_accel_group(self.accel_group)
78
self.prev_rev_action.connect("activate", self._back_clicked_cb)
79
self.prev_rev_action.connect_accelerator()
81
self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
82
self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
83
self.next_rev_action.set_accel_group(self.accel_group)
84
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
85
self.next_rev_action.connect_accelerator()
89
def set_revision(self, revid):
90
self.treeview.set_revision_id(revid)
93
"""Construct the window contents."""
94
vbox = gtk.VBox(spacing=0)
97
self.paned = gtk.VPaned()
98
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
99
self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
102
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
103
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
104
vbox.pack_start(self.construct_loading_msg(), expand=False, fill=True)
106
vbox.pack_start(self.paned, expand=True, fill=True)
107
vbox.set_focus_child(self.paned)
111
def construct_menubar(self):
112
menubar = gtk.MenuBar()
114
file_menu = gtk.Menu()
115
file_menuitem = gtk.MenuItem("_File")
116
file_menuitem.set_submenu(file_menu)
118
file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
119
file_menu_close.connect('activate', lambda x: self.destroy())
121
file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
122
file_menu_quit.connect('activate', lambda x: gtk.main_quit())
124
if self._parent is not None:
125
file_menu.add(file_menu_close)
126
file_menu.add(file_menu_quit)
128
edit_menu = gtk.Menu()
129
edit_menuitem = gtk.MenuItem("_Edit")
130
edit_menuitem.set_submenu(edit_menu)
132
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
134
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
135
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
137
edit_menu_globopts = gtk.MenuItem("Global Settings")
138
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
140
edit_menu.add(edit_menu_find)
141
edit_menu.add(edit_menu_branchopts)
142
edit_menu.add(edit_menu_globopts)
144
view_menu = gtk.Menu()
145
view_menuitem = gtk.MenuItem("_View")
146
view_menuitem.set_submenu(view_menu)
148
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
149
view_menu_toolbar.set_active(True)
150
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
152
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
153
view_menu_compact.set_active(self.compact_view)
154
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
156
view_menu.add(view_menu_toolbar)
157
view_menu.add(view_menu_compact)
158
view_menu.add(gtk.SeparatorMenuItem())
160
for (label, name) in [("Revision _Number", "revno"), ("_Date", "date")]:
161
col = gtk.CheckMenuItem("Show " + label + " Column")
162
col.set_active(self.treeview.get_property(name + "-column-visible"))
163
col.connect('toggled', self._col_visibility_changed, name)
167
go_menu.set_accel_group(self.accel_group)
168
go_menuitem = gtk.MenuItem("_Go")
169
go_menuitem.set_submenu(go_menu)
171
go_menu_next = self.next_rev_action.create_menu_item()
172
go_menu_prev = self.prev_rev_action.create_menu_item()
174
tags_menu = gtk.Menu()
175
go_menu_tags = gtk.MenuItem("_Tags")
176
go_menu_tags.set_submenu(tags_menu)
178
if self.branch.supports_tags():
179
tags = self.branch.tags.get_tag_dict().items()
182
for tag, revid in tags:
183
tag_item = gtk.MenuItem(tag)
184
tag_item.connect('activate', self._tag_selected_cb, revid)
185
tags_menu.add(tag_item)
187
go_menu_tags.set_sensitive(len(tags) != 0)
189
go_menu_tags.set_sensitive(False)
191
go_menu.add(go_menu_next)
192
go_menu.add(go_menu_prev)
193
go_menu.add(gtk.SeparatorMenuItem())
194
go_menu.add(go_menu_tags)
196
revision_menu = gtk.Menu()
197
revision_menuitem = gtk.MenuItem("_Revision")
198
revision_menuitem.set_submenu(revision_menu)
200
revision_menu_diff = gtk.MenuItem("View Changes")
201
revision_menu_diff.connect('activate',
202
lambda w: self.treeview.show_diff())
204
revision_menu_tag = gtk.MenuItem("Tag Revision")
205
revision_menu_tag.connect('activate', self._tag_revision_cb)
207
revision_menu.add(revision_menu_tag)
208
revision_menu.add(revision_menu_diff)
210
branch_menu = gtk.Menu()
211
branch_menuitem = gtk.MenuItem("_Branch")
212
branch_menuitem.set_submenu(branch_menu)
214
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
215
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
217
menubar.add(file_menuitem)
218
menubar.add(edit_menuitem)
219
menubar.add(view_menuitem)
220
menubar.add(go_menuitem)
221
menubar.add(revision_menuitem)
222
menubar.add(branch_menuitem)
227
def construct_loading_msg(self):
228
image_loading = gtk.image_new_from_stock(gtk.STOCK_REFRESH,
229
gtk.ICON_SIZE_BUTTON)
232
label_loading = gtk.Label(_("Please wait, loading ancestral graph..."))
233
label_loading.set_alignment(0.0, 0.5)
236
self.loading_msg_box = gtk.HBox()
237
self.loading_msg_box.set_spacing(5)
238
self.loading_msg_box.set_border_width(5)
239
self.loading_msg_box.pack_start(image_loading, False, False)
240
self.loading_msg_box.pack_start(label_loading, True, True)
241
self.loading_msg_box.show()
243
return self.loading_msg_box
245
def construct_top(self):
246
"""Construct the top-half of the window."""
247
# FIXME: Make broken_line_length configurable
249
self.treeview = TreeView(self.branch, self.start, self.maxnum, self.compact_view)
251
self.treeview.connect('revision-selected',
252
self._treeselection_changed_cb)
254
self.treeview.connect('revisions-loaded',
255
lambda x: self.loading_msg_box.hide())
257
for col in ["revno", "date"]:
258
option = self.config.get_user_option(col + '-column-visible')
259
if option is not None:
260
self.treeview.set_property(col + '-column-visible', option == 'True')
264
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
265
align.set_padding(5, 0, 0, 0)
266
align.add(self.treeview)
271
def construct_navigation(self):
272
"""Construct the navigation buttons."""
273
self.toolbar = gtk.Toolbar()
274
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
276
self.prev_button = self.prev_rev_action.create_tool_item()
277
self.toolbar.insert(self.prev_button, -1)
279
self.next_button = self.next_rev_action.create_tool_item()
280
self.toolbar.insert(self.next_button, -1)
282
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
284
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
285
refresh_button.connect('clicked', self._refresh_clicked)
286
self.toolbar.insert(refresh_button, -1)
288
self.toolbar.show_all()
292
def construct_bottom(self):
293
"""Construct the bottom half of the window."""
294
from bzrlib.plugins.gtk.revisionview import RevisionView
295
self.revisionview = RevisionView(None, tags=[], show_children=True, branch=self.branch)
296
(width, height) = self.get_size()
297
self.revisionview.set_size_request(width, int(height / 2.5))
298
self.revisionview.show()
299
self.revisionview.set_show_callback(self._show_clicked_cb)
300
self.revisionview.set_go_callback(self._go_clicked_cb)
301
return self.revisionview
303
def _tag_selected_cb(self, menuitem, revid):
304
self.treeview.set_revision_id(revid)
306
def _treeselection_changed_cb(self, selection, *args):
307
"""callback for when the treeview changes."""
308
revision = self.treeview.get_revision()
309
parents = self.treeview.get_parents()
310
children = self.treeview.get_children()
312
if revision is not None:
313
prev_menu = gtk.Menu()
315
self.prev_rev_action.set_sensitive(True)
316
for parent_id in parents:
317
parent = self.branch.repository.get_revision(parent_id)
319
str = ' (' + parent.properties['branch-nick'] + ')'
323
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
324
item.connect('activate', self._set_revision_cb, parent_id)
328
self.prev_rev_action.set_sensitive(False)
331
self.prev_button.set_menu(prev_menu)
333
next_menu = gtk.Menu()
334
if len(children) > 0:
335
self.next_rev_action.set_sensitive(True)
336
for child_id in children:
337
child = self.branch.repository.get_revision(child_id)
339
str = ' (' + child.properties['branch-nick'] + ')'
343
item = gtk.MenuItem(child.message.split("\n")[0] + str)
344
item.connect('activate', self._set_revision_cb, child_id)
348
self.next_rev_action.set_sensitive(False)
351
self.next_button.set_menu(next_menu)
354
if self.branch.supports_tags():
355
tagdict = self.branch.tags.get_reverse_tag_dict()
356
if tagdict.has_key(revision.revision_id):
357
tags = tagdict[revision.revision_id]
358
self.revisionview.set_revision(revision, tags, children)
360
def _back_clicked_cb(self, *args):
361
"""Callback for when the back button is clicked."""
364
def _fwd_clicked_cb(self, *args):
365
"""Callback for when the forward button is clicked."""
366
self.treeview.forward()
368
def _go_clicked_cb(self, revid):
369
"""Callback for when the go button for a parent is clicked."""
370
self.treeview.set_revision_id(revid)
372
def _show_clicked_cb(self, revid, parentid):
373
"""Callback for when the show button for a parent is clicked."""
374
self.treeview.show_diff(revid, parentid)
375
self.treeview.grab_focus()
377
def _set_revision_cb(self, w, revision_id):
378
self.treeview.set_revision_id(revision_id)
380
def _brokenlines_toggled_cb(self, button):
381
self.compact_view = button.get_active()
383
if self.compact_view:
388
self.config.set_user_option('viz-compact-view', option)
389
self.treeview.set_property('compact', self.compact_view)
390
self.treeview.refresh()
392
def _tag_revision_cb(self, w):
394
self.treeview.set_sensitive(False)
396
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
397
response = dialog.run()
398
if response != gtk.RESPONSE_NONE:
401
if response == gtk.RESPONSE_OK:
403
self.branch.lock_write()
404
self.branch.tags.set_tag(dialog.tagname, dialog._revid)
411
self.branch.lock_read()
412
self.treeview.emit("revision-selected")
413
self.treeview.set_sensitive(True)
415
def _col_visibility_changed(self, col, property):
416
self.config.set_user_option(property + '-column-visible', col.get_active())
417
self.treeview.set_property(property + '-column-visible', col.get_active())
419
def _toolbar_visibility_changed(self, col):
425
def _show_about_cb(self, w):
426
dialog = AboutDialog()
427
dialog.connect('response', lambda d,r: d.destroy())
430
def _refresh_clicked(self, w):
431
self.treeview.update()