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
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_prefs = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
137
edit_menu_prefs.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_prefs)
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
help_menu = gtk.Menu()
217
help_menuitem = gtk.MenuItem("_Help")
218
help_menuitem.set_submenu(help_menu)
220
help_menu_about = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
221
help_menu_about.connect('activate', self._show_about_cb)
223
help_menu.add(help_menu_about)
225
menubar.add(file_menuitem)
226
menubar.add(edit_menuitem)
227
menubar.add(view_menuitem)
228
menubar.add(go_menuitem)
229
menubar.add(revision_menuitem)
230
menubar.add(branch_menuitem)
231
menubar.add(help_menuitem)
236
def construct_loading_msg(self):
237
image_loading = gtk.image_new_from_stock(gtk.STOCK_REFRESH,
238
gtk.ICON_SIZE_BUTTON)
241
label_loading = gtk.Label(_("Please wait, loading ancestral graph..."))
242
label_loading.set_alignment(0.0, 0.5)
245
self.loading_msg_box = gtk.HBox()
246
self.loading_msg_box.set_spacing(5)
247
self.loading_msg_box.set_border_width(5)
248
self.loading_msg_box.pack_start(image_loading, False, False)
249
self.loading_msg_box.pack_start(label_loading, True, True)
250
self.loading_msg_box.show()
252
return self.loading_msg_box
254
def construct_top(self):
255
"""Construct the top-half of the window."""
256
# FIXME: Make broken_line_length configurable
258
self.treeview = TreeView(self.branch, self.start, self.maxnum, self.compact_view)
260
self.treeview.connect('revision-selected',
261
self._treeselection_changed_cb)
263
self.treeview.connect('revisions-loaded',
264
lambda x: self.loading_msg_box.hide())
266
for col in ["revno", "date"]:
267
option = self.config.get_user_option(col + '-column-visible')
268
if option is not None:
269
self.treeview.set_property(col + '-column-visible', option == 'True')
273
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
274
align.set_padding(5, 0, 0, 0)
275
align.add(self.treeview)
280
def construct_navigation(self):
281
"""Construct the navigation buttons."""
282
self.toolbar = gtk.Toolbar()
283
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
285
self.prev_button = self.prev_rev_action.create_tool_item()
286
self.toolbar.insert(self.prev_button, -1)
288
self.next_button = self.next_rev_action.create_tool_item()
289
self.toolbar.insert(self.next_button, -1)
291
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
293
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
294
refresh_button.connect('clicked', self._refresh_clicked)
295
self.toolbar.insert(refresh_button, -1)
297
self.toolbar.show_all()
301
def construct_bottom(self):
302
"""Construct the bottom half of the window."""
303
from bzrlib.plugins.gtk.revisionview import RevisionView
304
self.revisionview = RevisionView(None, tags=[], show_children=True, branch=self.branch)
305
(width, height) = self.get_size()
306
self.revisionview.set_size_request(width, int(height / 2.5))
307
self.revisionview.show()
308
self.revisionview.set_show_callback(self._show_clicked_cb)
309
self.revisionview.set_go_callback(self._go_clicked_cb)
310
return self.revisionview
312
def _tag_selected_cb(self, menuitem, revid):
313
self.treeview.set_revision_id(revid)
315
def _treeselection_changed_cb(self, selection, *args):
316
"""callback for when the treeview changes."""
317
revision = self.treeview.get_revision()
318
parents = self.treeview.get_parents()
319
children = self.treeview.get_children()
321
if revision is not None:
322
prev_menu = gtk.Menu()
324
self.prev_rev_action.set_sensitive(True)
325
for parent_id in parents:
326
parent = self.branch.repository.get_revision(parent_id)
328
str = ' (' + parent.properties['branch-nick'] + ')'
332
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
333
item.connect('activate', self._set_revision_cb, parent_id)
337
self.prev_rev_action.set_sensitive(False)
340
self.prev_button.set_menu(prev_menu)
342
next_menu = gtk.Menu()
343
if len(children) > 0:
344
self.next_rev_action.set_sensitive(True)
345
for child_id in children:
346
child = self.branch.repository.get_revision(child_id)
348
str = ' (' + child.properties['branch-nick'] + ')'
352
item = gtk.MenuItem(child.message.split("\n")[0] + str)
353
item.connect('activate', self._set_revision_cb, child_id)
357
self.next_rev_action.set_sensitive(False)
360
self.next_button.set_menu(next_menu)
363
if self.branch.supports_tags():
364
tagdict = self.branch.tags.get_reverse_tag_dict()
365
if tagdict.has_key(revision.revision_id):
366
tags = tagdict[revision.revision_id]
367
self.revisionview.set_revision(revision, tags, children)
369
def _back_clicked_cb(self, *args):
370
"""Callback for when the back button is clicked."""
373
def _fwd_clicked_cb(self, *args):
374
"""Callback for when the forward button is clicked."""
375
self.treeview.forward()
377
def _go_clicked_cb(self, revid):
378
"""Callback for when the go button for a parent is clicked."""
379
self.treeview.set_revision_id(revid)
381
def _show_clicked_cb(self, revid, parentid):
382
"""Callback for when the show button for a parent is clicked."""
383
self.treeview.show_diff(revid, parentid)
384
self.treeview.grab_focus()
386
def _set_revision_cb(self, w, revision_id):
387
self.treeview.set_revision_id(revision_id)
389
def _brokenlines_toggled_cb(self, button):
390
self.compact_view = button.get_active()
392
if self.compact_view:
397
self.config.set_user_option('viz-compact-view', option)
398
self.treeview.set_property('compact', self.compact_view)
399
self.treeview.refresh()
401
def _tag_revision_cb(self, w):
403
self.treeview.set_sensitive(False)
405
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
406
response = dialog.run()
407
if response != gtk.RESPONSE_NONE:
410
if response == gtk.RESPONSE_OK:
412
self.branch.lock_write()
413
self.branch.tags.set_tag(dialog.tagname, dialog._revid)
420
self.branch.lock_read()
421
self.treeview.emit("revision-selected")
422
self.treeview.set_sensitive(True)
424
def _col_visibility_changed(self, col, property):
425
self.config.set_user_option(property + '-column-visible', col.get_active())
426
self.treeview.set_property(property + '-column-visible', col.get_active())
428
def _toolbar_visibility_changed(self, col):
434
def _show_about_cb(self, w):
435
dialog = AboutDialog()
436
dialog.connect('response', lambda d,r: d.destroy())
439
def _refresh_clicked(self, w):
440
self.treeview.update()