16
16
from bzrlib.plugins.gtk.window import Window
17
from bzrlib.plugins.gtk import icon_path
18
17
from bzrlib.plugins.gtk.tags import AddTagDialog
19
18
from bzrlib.plugins.gtk.preferences import PreferencesWindow
20
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
21
from bzrlib.revision import Revision, NULL_REVISION
19
from bzrlib.revision import Revision
22
20
from bzrlib.config import BranchConfig
23
21
from bzrlib.config import GlobalConfig
22
from treeview import TreeView
23
from about import AboutDialog
25
25
class BranchWindow(Window):
29
29
for a particular branch.
32
def __init__(self, branch, start_revs, maxnum, parent=None):
32
def __init__(self, branch, start, maxnum, parent=None):
33
33
"""Create a new BranchWindow.
35
35
:param branch: Branch object for branch to show.
36
:param start_revs: Revision ids of top revisions.
36
:param start: Revision id of top revision.
37
37
:param maxnum: Maximum number of revisions to display,
64
64
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
65
65
self.set_icon(icon)
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)
69
gtk.accel_map_add_entry("<viz>/View/Refresh", gtk.keysyms.F5, 0)
71
67
self.accel_group = gtk.AccelGroup()
72
68
self.add_accel_group(self.accel_group)
74
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
76
self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
77
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
78
self.prev_rev_action.set_accel_group(self.accel_group)
79
self.prev_rev_action.connect("activate", self._back_clicked_cb)
80
self.prev_rev_action.connect_accelerator()
82
self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
83
self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
84
self.next_rev_action.set_accel_group(self.accel_group)
85
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
86
self.next_rev_action.connect_accelerator()
88
self.refresh_action = gtk.Action("refresh", "_Refresh", "Refresh view", gtk.STOCK_REFRESH)
89
self.refresh_action.set_accel_path("<viz>/View/Refresh")
90
self.refresh_action.set_accel_group(self.accel_group)
91
self.refresh_action.connect("activate", self._refresh_clicked)
92
self.refresh_action.connect_accelerator()
96
72
def set_revision(self, revid):
101
77
vbox = gtk.VBox(spacing=0)
80
top = self.construct_top()
82
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
83
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
84
vbox.pack_start(self.construct_loading_msg(), expand=False, fill=True)
104
86
self.paned = gtk.VPaned()
105
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
87
self.paned.pack1(top, resize=True, shrink=False)
106
88
self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
109
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
110
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
112
90
vbox.pack_start(self.paned, expand=True, fill=True)
113
91
vbox.set_focus_child(self.paned)
140
117
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
141
118
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
143
edit_menu_globopts = gtk.MenuItem("Global Settings")
144
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
120
edit_menu_prefs = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
121
edit_menu_prefs.connect('activate', lambda x: PreferencesWindow().show())
146
123
edit_menu.add(edit_menu_find)
147
124
edit_menu.add(edit_menu_branchopts)
148
edit_menu.add(edit_menu_globopts)
125
edit_menu.add(edit_menu_prefs)
150
127
view_menu = gtk.Menu()
151
128
view_menuitem = gtk.MenuItem("_View")
152
129
view_menuitem.set_submenu(view_menu)
154
view_menu_refresh = self.refresh_action.create_menu_item()
155
view_menu_refresh.connect('activate', self._refresh_clicked)
157
view_menu.add(view_menu_refresh)
158
view_menu.add(gtk.SeparatorMenuItem())
160
131
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
161
132
view_menu_toolbar.set_active(True)
162
133
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
169
140
view_menu.add(view_menu_compact)
170
141
view_menu.add(gtk.SeparatorMenuItem())
172
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
173
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
175
# Revision numbers are pointless if there are multiple branches
176
if len(self.start_revs) > 1:
177
self.mnu_show_revno_column.set_sensitive(False)
178
self.treeview.set_property('revno-column-visible', False)
180
for (col, name) in [(self.mnu_show_revno_column, "revno"),
181
(self.mnu_show_date_column, "date")]:
143
for (label, name) in [("Revision _Number", "revno"), ("_Date", "date")]:
144
col = gtk.CheckMenuItem("Show " + label + " Column")
182
145
col.set_active(self.treeview.get_property(name + "-column-visible"))
183
146
col.connect('toggled', self._col_visibility_changed, name)
184
147
view_menu.add(col)
186
149
go_menu = gtk.Menu()
187
go_menu.set_accel_group(self.accel_group)
188
150
go_menuitem = gtk.MenuItem("_Go")
189
151
go_menuitem.set_submenu(go_menu)
191
go_menu_next = self.next_rev_action.create_menu_item()
192
go_menu_prev = self.prev_rev_action.create_menu_item()
194
tag_image = gtk.Image()
195
tag_image.set_from_file(icon_path("tag-16.png"))
196
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
197
self.go_menu_tags.set_image(tag_image)
200
go_menu.add(go_menu_next)
201
go_menu.add(go_menu_prev)
153
go_menu_back = gtk.ImageMenuItem(gtk.STOCK_GO_DOWN)
154
go_menu_back.connect("activate", self._back_clicked_cb)
156
go_menu_forward = gtk.ImageMenuItem(gtk.STOCK_GO_UP)
157
go_menu_forward.connect("activate", self._fwd_clicked_cb)
159
tags_menu = gtk.Menu()
160
go_menu_tags = gtk.MenuItem("_Tags")
161
go_menu_tags.set_submenu(tags_menu)
163
if self.branch.supports_tags():
164
for (tag, revid) in self.branch.tags.get_tag_dict().items():
165
tag_item = gtk.MenuItem(tag)
166
tag_item.connect('activate', self._tag_selected_cb, revid)
167
tags_menu.add(tag_item)
169
go_menu.add(go_menu_back)
170
go_menu.add(go_menu_forward)
202
171
go_menu.add(gtk.SeparatorMenuItem())
203
go_menu.add(self.go_menu_tags)
172
go_menu.add(go_menu_tags)
205
174
revision_menu = gtk.Menu()
206
175
revision_menuitem = gtk.MenuItem("_Revision")
209
178
revision_menu_diff = gtk.MenuItem("View Changes")
210
179
revision_menu_diff.connect('activate',
213
revision_menu_compare = gtk.MenuItem("Compare with...")
214
revision_menu_compare.connect('activate',
215
self._compare_with_cb)
180
lambda w: self.treeview.show_diff())
217
182
revision_menu_tag = gtk.MenuItem("Tag Revision")
218
183
revision_menu_tag.connect('activate', self._tag_revision_cb)
220
185
revision_menu.add(revision_menu_tag)
221
186
revision_menu.add(revision_menu_diff)
222
revision_menu.add(revision_menu_compare)
224
188
branch_menu = gtk.Menu()
225
189
branch_menuitem = gtk.MenuItem("_Branch")
232
196
help_menuitem = gtk.MenuItem("_Help")
233
197
help_menuitem.set_submenu(help_menu)
235
help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
236
help_about_menuitem.connect('activate', self._about_dialog_cb)
238
help_menu.add(help_about_menuitem)
199
help_menu_about = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
200
help_menu_about.connect('activate', self._show_about_cb)
202
help_menu.add(help_menu_about)
240
204
menubar.add(file_menuitem)
241
205
menubar.add(edit_menuitem)
242
206
menubar.add(view_menuitem)
247
211
menubar.show_all()
215
def construct_loading_msg(self):
216
image_loading = gtk.image_new_from_stock(gtk.STOCK_REFRESH,
217
gtk.ICON_SIZE_BUTTON)
220
label_loading = gtk.Label(_("Please wait, loading ancestral graph..."))
221
label_loading.set_alignment(0.0, 0.5)
224
self.loading_msg_box = gtk.HBox()
225
self.loading_msg_box.set_spacing(5)
226
self.loading_msg_box.set_border_width(5)
227
self.loading_msg_box.pack_start(image_loading, False, False)
228
self.loading_msg_box.pack_start(label_loading, True, True)
229
self.loading_msg_box.show()
231
return self.loading_msg_box
251
233
def construct_top(self):
252
234
"""Construct the top-half of the window."""
253
235
# FIXME: Make broken_line_length configurable
255
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
257
self.treeview.connect('revision-selected',
236
if self.compact_view:
241
self.treeview = TreeView(self.branch, self.start, self.maxnum, brokenlines)
243
self.treeview.connect("revision-selected",
258
244
self._treeselection_changed_cb)
259
self.treeview.connect('revision-activated',
260
self._tree_revision_activated)
262
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
264
for col in ["revno", "date"]:
265
option = self.config.get_user_option(col + '-column-visible')
266
if option is not None:
267
self.treeview.set_property(col + '-column-visible', option == 'True')
269
self.treeview.set_property(col + '-column-visible', False)
246
self.treeview.connect('revisions-loaded',
247
lambda x: self.loading_msg_box.hide())
271
249
self.treeview.show()
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
253
def construct_navigation(self):
281
254
"""Construct the navigation buttons."""
282
255
self.toolbar = gtk.Toolbar()
283
256
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)
258
self.back_button = gtk.MenuToolButton(stock_id=gtk.STOCK_GO_DOWN)
259
self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
261
self.back_button.connect("clicked", self._back_clicked_cb)
262
self.toolbar.insert(self.back_button, -1)
264
self.fwd_button = gtk.MenuToolButton(stock_id=gtk.STOCK_GO_UP)
265
self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
267
self.fwd_button.connect("clicked", self._fwd_clicked_cb)
268
self.toolbar.insert(self.fwd_button, -1)
297
270
self.toolbar.show_all()
301
274
def construct_bottom(self):
302
275
"""Construct the bottom half of the window."""
303
276
from bzrlib.plugins.gtk.revisionview import RevisionView
304
self.revisionview = RevisionView(branch=self.branch)
277
self.revisionview = RevisionView(None, tags=[], show_children=True, branch=self.branch)
305
278
(width, height) = self.get_size()
306
279
self.revisionview.set_size_request(width, int(height / 2.5))
307
280
self.revisionview.show()
308
281
self.revisionview.set_show_callback(self._show_clicked_cb)
309
self.revisionview.connect('notify::revision', self._go_clicked_cb)
310
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
282
self.revisionview.set_go_callback(self._go_clicked_cb)
311
283
return self.revisionview
313
285
def _tag_selected_cb(self, menuitem, revid):
319
291
parents = self.treeview.get_parents()
320
292
children = self.treeview.get_children()
322
if revision and revision != NULL_REVISION:
323
prev_menu = gtk.Menu()
294
if revision is not None:
295
back_menu = gtk.Menu()
324
296
if len(parents) > 0:
325
self.prev_rev_action.set_sensitive(True)
297
self.back_button.set_sensitive(True)
326
298
for parent_id in parents:
327
if parent_id and parent_id != NULL_REVISION:
328
parent = self.branch.repository.get_revision(parent_id)
330
str = ' (' + parent.properties['branch-nick'] + ')'
299
parent = self.branch.repository.get_revision(parent_id)
301
str = ' (' + parent.properties['branch-nick'] + ')'
334
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
335
item.connect('activate', self._set_revision_cb, parent_id)
305
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
306
item.connect('activate', self._set_revision_cb, parent_id)
339
self.prev_rev_action.set_sensitive(False)
342
self.prev_button.set_menu(prev_menu)
344
next_menu = gtk.Menu()
310
self.back_button.set_sensitive(False)
313
self.back_button.set_menu(back_menu)
315
fwd_menu = gtk.Menu()
345
316
if len(children) > 0:
346
self.next_rev_action.set_sensitive(True)
317
self.fwd_button.set_sensitive(True)
347
318
for child_id in children:
348
319
child = self.branch.repository.get_revision(child_id)
354
325
item = gtk.MenuItem(child.message.split("\n")[0] + str)
355
326
item.connect('activate', self._set_revision_cb, child_id)
359
self.next_rev_action.set_sensitive(False)
362
self.next_button.set_menu(next_menu)
364
self.revisionview.set_revision(revision)
365
self.revisionview.set_children(children)
367
def _tree_revision_activated(self, widget, path, col):
368
# TODO: more than one parent
369
"""Callback for when a treeview row gets activated."""
370
revision = self.treeview.get_revision()
371
parents = self.treeview.get_parents()
373
if len(parents) == 0:
376
parent_id = parents[0]
378
self.show_diff(revision.revision_id, parent_id)
379
self.treeview.grab_focus()
381
def _menu_diff_cb(self,w):
382
(path, focus) = self.treeview.treeview.get_cursor()
383
revid = self.treeview.model[path][treemodel.REVID]
385
parentids = self.branch.repository.revision_parents(revid)
387
if len(parentids) == 0:
388
parentid = NULL_REVISION
390
parentid = parentids[0]
392
self.show_diff(revid,parentid)
330
self.fwd_button.set_sensitive(False)
333
self.fwd_button.set_menu(fwd_menu)
336
if self.branch.supports_tags():
337
tagdict = self.branch.tags.get_reverse_tag_dict()
338
if tagdict.has_key(revision.revision_id):
339
tags = tagdict[revision.revision_id]
340
self.revisionview.set_revision(revision, tags, children)
394
342
def _back_clicked_cb(self, *args):
395
343
"""Callback for when the back button is clicked."""
399
347
"""Callback for when the forward button is clicked."""
400
348
self.treeview.forward()
402
def _go_clicked_cb(self, w, p):
350
def _go_clicked_cb(self, revid):
403
351
"""Callback for when the go button for a parent is clicked."""
404
if self.revisionview.get_revision() is not None:
405
self.treeview.set_revision(self.revisionview.get_revision())
352
self.treeview.set_revision_id(revid)
407
354
def _show_clicked_cb(self, revid, parentid):
408
355
"""Callback for when the show button for a parent is clicked."""
409
self.show_diff(revid, parentid)
356
self.treeview.show_diff(revid, parentid)
410
357
self.treeview.grab_focus()
412
def _compare_with_cb(self,w):
413
"""Callback for revision 'compare with' menu. Will show a small
414
dialog with branch revisions to compare with selected revision in TreeView"""
416
from bzrlib.plugins.gtk.revbrowser import RevisionBrowser
418
rb = RevisionBrowser(self.branch,self)
421
if ret == gtk.RESPONSE_OK:
422
(path, focus) = self.treeview.treeview.get_cursor()
423
revid = self.treeview.model[path][treemodel.REVID]
424
self.show_diff(revid, rb.selected_revid)
428
359
def _set_revision_cb(self, w, revision_id):
429
360
self.treeview.set_revision_id(revision_id)
439
370
self.config.set_user_option('viz-compact-view', option)
440
self.treeview.set_property('compact', self.compact_view)
441
self.treeview.refresh()
372
revision = self.treeview.get_revision()
374
self.treeview.destroy()
375
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
377
gobject.idle_add(self.set_revision, revision.revision_id)
443
379
def _tag_revision_cb(self, w):
445
self.treeview.set_sensitive(False)
446
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
447
response = dialog.run()
448
if response != gtk.RESPONSE_NONE:
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:
387
self.branch.lock_write()
388
self.branch.tags.set_tag(dialog.tagname, dialog._revid)
451
if response == gtk.RESPONSE_OK:
452
self.treeview.add_tag(dialog.tagname, dialog._revid)
457
self.treeview.set_sensitive(True)
459
def _about_dialog_cb(self, w):
460
from bzrlib.plugins.gtk.about import AboutDialog
464
394
def _col_visibility_changed(self, col, property):
465
self.config.set_user_option(property + '-column-visible', col.get_active())
466
395
self.treeview.set_property(property + '-column-visible', col.get_active())
468
397
def _toolbar_visibility_changed(self, col):
475
404
dialog = AboutDialog()
476
405
dialog.connect('response', lambda d,r: d.destroy())
479
def _refresh_clicked(self, w):
480
self.treeview.refresh()
482
def _update_tags(self):
485
if self.branch.supports_tags():
486
tags = self.branch.tags.get_tag_dict().items()
489
for tag, revid in tags:
490
tag_image = gtk.Image()
491
tag_image.set_from_file(icon_path('tag-16.png'))
492
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
493
tag_item.set_image(tag_image)
494
tag_item.connect('activate', self._tag_selected_cb, revid)
496
self.go_menu_tags.set_submenu(menu)
498
self.go_menu_tags.set_sensitive(len(tags) != 0)
500
self.go_menu_tags.set_sensitive(False)
502
self.go_menu_tags.show_all()
504
def show_diff(self, revid=None, parentid=None):
505
"""Open a new window to show a diff between the given revisions."""
506
from bzrlib.plugins.gtk.diff import DiffWindow
507
window = DiffWindow(parent=self)
510
parentid = NULL_REVISION
512
rev_tree = self.branch.repository.revision_tree(revid)
513
parent_tree = self.branch.repository.revision_tree(parentid)
515
description = revid + " - " + self.branch.nick
516
window.set_diff(description, rev_tree, parent_tree)