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
for (tag, revid) in self.branch.tags.get_tag_dict().items():
179
tag_item = gtk.MenuItem(tag)
180
tag_item.connect('activate', self._tag_selected_cb, revid)
181
tags_menu.add(tag_item)
183
go_menu.add(go_menu_next)
184
go_menu.add(go_menu_prev)
185
go_menu.add(gtk.SeparatorMenuItem())
186
go_menu.add(go_menu_tags)
188
revision_menu = gtk.Menu()
189
revision_menuitem = gtk.MenuItem("_Revision")
190
revision_menuitem.set_submenu(revision_menu)
192
revision_menu_diff = gtk.MenuItem("View Changes")
193
revision_menu_diff.connect('activate',
194
lambda w: self.treeview.show_diff())
196
revision_menu_tag = gtk.MenuItem("Tag Revision")
197
revision_menu_tag.connect('activate', self._tag_revision_cb)
199
revision_menu.add(revision_menu_tag)
200
revision_menu.add(revision_menu_diff)
202
branch_menu = gtk.Menu()
203
branch_menuitem = gtk.MenuItem("_Branch")
204
branch_menuitem.set_submenu(branch_menu)
206
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
207
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
209
help_menu = gtk.Menu()
210
help_menuitem = gtk.MenuItem("_Help")
211
help_menuitem.set_submenu(help_menu)
213
help_menu_about = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
214
help_menu_about.connect('activate', self._show_about_cb)
216
help_menu.add(help_menu_about)
218
menubar.add(file_menuitem)
219
menubar.add(edit_menuitem)
220
menubar.add(view_menuitem)
221
menubar.add(go_menuitem)
222
menubar.add(revision_menuitem)
223
menubar.add(branch_menuitem)
224
menubar.add(help_menuitem)
229
def construct_loading_msg(self):
230
image_loading = gtk.image_new_from_stock(gtk.STOCK_REFRESH,
231
gtk.ICON_SIZE_BUTTON)
234
label_loading = gtk.Label(_("Please wait, loading ancestral graph..."))
235
label_loading.set_alignment(0.0, 0.5)
238
self.loading_msg_box = gtk.HBox()
239
self.loading_msg_box.set_spacing(5)
240
self.loading_msg_box.set_border_width(5)
241
self.loading_msg_box.pack_start(image_loading, False, False)
242
self.loading_msg_box.pack_start(label_loading, True, True)
243
self.loading_msg_box.show()
245
return self.loading_msg_box
247
def construct_top(self):
248
"""Construct the top-half of the window."""
249
# FIXME: Make broken_line_length configurable
250
if self.compact_view:
255
self.treeview = TreeView(self.branch, self.start, self.maxnum, brokenlines)
257
self.treeview.connect('revision-selected',
258
self._treeselection_changed_cb)
260
self.treeview.connect('revisions-loaded',
261
lambda x: self.loading_msg_box.hide())
263
for col in ["revno", "date"]:
264
option = self.config.get_user_option(col + '-column-visible')
265
if option is not None:
266
self.treeview.set_property(col + '-column-visible', option == 'True')
270
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
271
align.set_padding(5, 0, 0, 0)
272
align.add(self.treeview)
277
def construct_navigation(self):
278
"""Construct the navigation buttons."""
279
self.toolbar = gtk.Toolbar()
280
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
282
self.prev_button = self.prev_rev_action.create_tool_item()
283
self.toolbar.insert(self.prev_button, -1)
285
self.next_button = self.next_rev_action.create_tool_item()
286
self.toolbar.insert(self.next_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)
390
revision = self.treeview.get_revision()
392
self.paned.get_child1().destroy()
393
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
395
gobject.idle_add(self.set_revision, revision.revision_id)
397
def _tag_revision_cb(self, w):
399
self.treeview.set_sensitive(False)
401
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
402
response = dialog.run()
403
if response != gtk.RESPONSE_NONE:
406
if response == gtk.RESPONSE_OK:
408
self.branch.lock_write()
409
self.branch.tags.set_tag(dialog.tagname, dialog._revid)
416
self.branch.lock_read()
417
self.treeview.emit("revision-selected")
418
self.treeview.set_sensitive(True)
420
def _col_visibility_changed(self, col, property):
421
self.config.set_user_option(property + '-column-visible', col.get_active())
422
self.treeview.set_property(property + '-column-visible', col.get_active())
424
def _toolbar_visibility_changed(self, col):
430
def _show_about_cb(self, w):
431
dialog = AboutDialog()
432
dialog.connect('response', lambda d,r: d.destroy())