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, treemodel
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)
104
vbox.pack_start(self.paned, expand=True, fill=True)
105
vbox.set_focus_child(self.paned)
109
def construct_menubar(self):
110
menubar = gtk.MenuBar()
112
file_menu = gtk.Menu()
113
file_menuitem = gtk.MenuItem("_File")
114
file_menuitem.set_submenu(file_menu)
116
file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
117
file_menu_close.connect('activate', lambda x: self.destroy())
119
file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
120
file_menu_quit.connect('activate', lambda x: gtk.main_quit())
122
if self._parent is not None:
123
file_menu.add(file_menu_close)
124
file_menu.add(file_menu_quit)
126
edit_menu = gtk.Menu()
127
edit_menuitem = gtk.MenuItem("_Edit")
128
edit_menuitem.set_submenu(edit_menu)
130
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
132
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
133
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
135
edit_menu_globopts = gtk.MenuItem("Global Settings")
136
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
138
edit_menu.add(edit_menu_find)
139
edit_menu.add(edit_menu_branchopts)
140
edit_menu.add(edit_menu_globopts)
142
view_menu = gtk.Menu()
143
view_menuitem = gtk.MenuItem("_View")
144
view_menuitem.set_submenu(view_menu)
146
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
147
view_menu_toolbar.set_active(True)
148
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
150
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
151
view_menu_compact.set_active(self.compact_view)
152
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
154
view_menu.add(view_menu_toolbar)
155
view_menu.add(view_menu_compact)
156
view_menu.add(gtk.SeparatorMenuItem())
158
for (label, name) in [("Revision _Number", "revno"), ("_Date", "date")]:
159
col = gtk.CheckMenuItem("Show " + label + " Column")
160
col.set_active(self.treeview.get_property(name + "-column-visible"))
161
col.connect('toggled', self._col_visibility_changed, name)
165
go_menu.set_accel_group(self.accel_group)
166
go_menuitem = gtk.MenuItem("_Go")
167
go_menuitem.set_submenu(go_menu)
169
go_menu_next = self.next_rev_action.create_menu_item()
170
go_menu_prev = self.prev_rev_action.create_menu_item()
172
self.go_menu_tags = gtk.MenuItem("_Tags")
175
go_menu.add(go_menu_next)
176
go_menu.add(go_menu_prev)
177
go_menu.add(gtk.SeparatorMenuItem())
178
go_menu.add(self.go_menu_tags)
180
revision_menu = gtk.Menu()
181
revision_menuitem = gtk.MenuItem("_Revision")
182
revision_menuitem.set_submenu(revision_menu)
184
revision_menu_diff = gtk.MenuItem("View Changes")
185
revision_menu_diff.connect('activate',
186
lambda w: self.treeview.show_diff())
188
revision_menu_tag = gtk.MenuItem("Tag Revision")
189
revision_menu_tag.connect('activate', self._tag_revision_cb)
191
revision_menu.add(revision_menu_tag)
192
revision_menu.add(revision_menu_diff)
194
branch_menu = gtk.Menu()
195
branch_menuitem = gtk.MenuItem("_Branch")
196
branch_menuitem.set_submenu(branch_menu)
198
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
199
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
201
help_menu = gtk.Menu()
202
help_menuitem = gtk.MenuItem("_Help")
203
help_menuitem.set_submenu(help_menu)
205
help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
206
help_about_menuitem.connect('activate', self._about_dialog_cb)
208
help_menu.add(help_about_menuitem)
210
menubar.add(file_menuitem)
211
menubar.add(edit_menuitem)
212
menubar.add(view_menuitem)
213
menubar.add(go_menuitem)
214
menubar.add(revision_menuitem)
215
menubar.add(branch_menuitem)
216
menubar.add(help_menuitem)
221
def construct_top(self):
222
"""Construct the top-half of the window."""
223
# FIXME: Make broken_line_length configurable
225
self.treeview = TreeView(self.branch, self.start, self.maxnum, self.compact_view)
227
self.treeview.connect('revision-selected',
228
self._treeselection_changed_cb)
229
self.treeview.connect('revision-activated',
230
self._tree_revision_activated)
232
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
234
for col in ["revno", "date"]:
235
option = self.config.get_user_option(col + '-column-visible')
236
if option is not None:
237
self.treeview.set_property(col + '-column-visible', option == 'True')
241
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
242
align.set_padding(5, 0, 0, 0)
243
align.add(self.treeview)
248
def construct_navigation(self):
249
"""Construct the navigation buttons."""
250
self.toolbar = gtk.Toolbar()
251
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
253
self.prev_button = self.prev_rev_action.create_tool_item()
254
self.toolbar.insert(self.prev_button, -1)
256
self.next_button = self.next_rev_action.create_tool_item()
257
self.toolbar.insert(self.next_button, -1)
259
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
261
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
262
refresh_button.connect('clicked', self._refresh_clicked)
263
self.toolbar.insert(refresh_button, -1)
265
self.toolbar.show_all()
269
def construct_bottom(self):
270
"""Construct the bottom half of the window."""
271
from bzrlib.plugins.gtk.revisionview import RevisionView
272
self.revisionview = RevisionView(branch=self.branch)
273
(width, height) = self.get_size()
274
self.revisionview.set_size_request(width, int(height / 2.5))
275
self.revisionview.show()
276
self.revisionview.set_show_callback(self._show_clicked_cb)
277
self.revisionview.connect('notify::revision', self._go_clicked_cb)
278
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
279
return self.revisionview
281
def _tag_selected_cb(self, menuitem, revid):
282
self.treeview.set_revision_id(revid)
284
def _treeselection_changed_cb(self, selection, *args):
285
"""callback for when the treeview changes."""
286
revision = self.treeview.get_revision()
287
parents = self.treeview.get_parents()
288
children = self.treeview.get_children()
290
if revision is not None:
291
prev_menu = gtk.Menu()
293
self.prev_rev_action.set_sensitive(True)
294
for parent_id in parents:
295
parent = self.branch.repository.get_revision(parent_id)
297
str = ' (' + parent.properties['branch-nick'] + ')'
301
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
302
item.connect('activate', self._set_revision_cb, parent_id)
306
self.prev_rev_action.set_sensitive(False)
309
self.prev_button.set_menu(prev_menu)
311
next_menu = gtk.Menu()
312
if len(children) > 0:
313
self.next_rev_action.set_sensitive(True)
314
for child_id in children:
315
child = self.branch.repository.get_revision(child_id)
317
str = ' (' + child.properties['branch-nick'] + ')'
321
item = gtk.MenuItem(child.message.split("\n")[0] + str)
322
item.connect('activate', self._set_revision_cb, child_id)
326
self.next_rev_action.set_sensitive(False)
329
self.next_button.set_menu(next_menu)
331
self.revisionview.set_revision(revision)
332
self.revisionview.set_children(children)
334
def _tree_revision_activated(self, widget, path, col):
335
# TODO: more than one parent
336
"""Callback for when a treeview row gets activated."""
337
revision = self.treeview.get_revision()
338
parents = self.treeview.get_parents()
340
if len(parents) == 0:
343
parent_id = parents[0]
345
self.show_diff(revision.revision_id, parent_id)
346
self.treeview.grab_focus()
349
def _back_clicked_cb(self, *args):
350
"""Callback for when the back button is clicked."""
353
def _fwd_clicked_cb(self, *args):
354
"""Callback for when the forward button is clicked."""
355
self.treeview.forward()
357
def _go_clicked_cb(self, w, p):
358
"""Callback for when the go button for a parent is clicked."""
359
if self.revisionview.get_revision() is not None:
360
self.treeview.set_revision(self.revisionview.get_revision())
362
def _show_clicked_cb(self, revid, parentid):
363
"""Callback for when the show button for a parent is clicked."""
364
self.show_diff(revid, parentid)
365
self.treeview.grab_focus()
367
def _set_revision_cb(self, w, revision_id):
368
self.treeview.set_revision_id(revision_id)
370
def _brokenlines_toggled_cb(self, button):
371
self.compact_view = button.get_active()
373
if self.compact_view:
378
self.config.set_user_option('viz-compact-view', option)
379
self.treeview.set_property('compact', self.compact_view)
380
self.treeview.refresh()
382
def _tag_revision_cb(self, w):
384
self.treeview.set_sensitive(False)
385
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
386
response = dialog.run()
387
if response != gtk.RESPONSE_NONE:
390
if response == gtk.RESPONSE_OK:
391
self.treeview.add_tag(dialog.tagname, dialog._revid)
396
self.treeview.set_sensitive(True)
398
def _about_dialog_cb(self, w):
399
from bzrlib.plugins.gtk.about import AboutDialog
403
def _col_visibility_changed(self, col, property):
404
self.config.set_user_option(property + '-column-visible', col.get_active())
405
self.treeview.set_property(property + '-column-visible', col.get_active())
407
def _toolbar_visibility_changed(self, col):
413
def _show_about_cb(self, w):
414
dialog = AboutDialog()
415
dialog.connect('response', lambda d,r: d.destroy())
418
def _refresh_clicked(self, w):
419
self.treeview.refresh()
421
def _update_tags(self):
424
if self.branch.supports_tags():
425
tags = self.branch.tags.get_tag_dict().items()
428
for tag, revid in tags:
429
tag_item = gtk.MenuItem(tag)
430
tag_item.connect('activate', self._tag_selected_cb, revid)
432
self.go_menu_tags.set_submenu(menu)
434
self.go_menu_tags.set_sensitive(len(tags) != 0)
436
self.go_menu_tags.set_sensitive(False)
438
self.go_menu_tags.show_all()
440
def show_diff(self, revid=None, parentid=None):
441
"""Open a new window to show a diff between the given revisions."""
442
from bzrlib.plugins.gtk.diff import DiffWindow
443
window = DiffWindow(parent=self)
446
parentid = NULL_REVISION
448
rev_tree = self.branch.repository.revision_tree(revid)
449
parent_tree = self.branch.repository.revision_tree(parentid)
451
description = revid + " - " + self.branch.nick
452
window.set_diff(description, rev_tree, parent_tree)