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
22
from bzrlib.config import BranchConfig, GlobalConfig
23
from bzrlib.revision import Revision, NULL_REVISION
24
from bzrlib.trace import mutter
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
26
25
class BranchWindow(Window):
30
29
for a particular branch.
33
def __init__(self, branch, start_revs, maxnum, parent=None):
32
def __init__(self, branch, start, maxnum, parent=None):
34
33
"""Create a new BranchWindow.
36
35
:param branch: Branch object for branch to show.
37
:param start_revs: Revision ids of top revisions.
36
:param start: Revision id of top revision.
38
37
:param maxnum: Maximum number of revisions to display,
65
64
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
66
65
self.set_icon(icon)
68
gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
69
gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
70
gtk.accel_map_add_entry("<viz>/View/Refresh", gtk.keysyms.F5, 0)
72
67
self.accel_group = gtk.AccelGroup()
73
68
self.add_accel_group(self.accel_group)
75
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
77
self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
78
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
79
self.prev_rev_action.set_accel_group(self.accel_group)
80
self.prev_rev_action.connect("activate", self._back_clicked_cb)
81
self.prev_rev_action.connect_accelerator()
83
self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
84
self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
85
self.next_rev_action.set_accel_group(self.accel_group)
86
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
87
self.next_rev_action.connect_accelerator()
89
self.refresh_action = gtk.Action("refresh", "_Refresh", "Refresh view", gtk.STOCK_REFRESH)
90
self.refresh_action.set_accel_path("<viz>/View/Refresh")
91
self.refresh_action.set_accel_group(self.accel_group)
92
self.refresh_action.connect("activate", self._refresh_clicked)
93
self.refresh_action.connect_accelerator()
97
72
def set_revision(self, revid):
102
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)
105
86
self.paned = gtk.VPaned()
106
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
87
self.paned.pack1(top, resize=True, shrink=False)
107
88
self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
110
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
111
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
113
90
vbox.pack_start(self.paned, expand=True, fill=True)
114
91
vbox.set_focus_child(self.paned)
141
117
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
142
118
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
144
edit_menu_globopts = gtk.MenuItem("Global Settings")
145
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())
147
123
edit_menu.add(edit_menu_find)
148
124
edit_menu.add(edit_menu_branchopts)
149
edit_menu.add(edit_menu_globopts)
125
edit_menu.add(edit_menu_prefs)
151
127
view_menu = gtk.Menu()
152
128
view_menuitem = gtk.MenuItem("_View")
153
129
view_menuitem.set_submenu(view_menu)
155
view_menu_refresh = self.refresh_action.create_menu_item()
156
view_menu_refresh.connect('activate', self._refresh_clicked)
158
view_menu.add(view_menu_refresh)
159
view_menu.add(gtk.SeparatorMenuItem())
161
131
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
162
132
view_menu_toolbar.set_active(True)
163
133
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
170
140
view_menu.add(view_menu_compact)
171
141
view_menu.add(gtk.SeparatorMenuItem())
173
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
174
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
176
# Revision numbers are pointless if there are multiple branches
177
if len(self.start_revs) > 1:
178
self.mnu_show_revno_column.set_sensitive(False)
179
self.treeview.set_property('revno-column-visible', False)
181
for (col, name) in [(self.mnu_show_revno_column, "revno"),
182
(self.mnu_show_date_column, "date")]:
143
for (label, name) in [("Revision _Number", "revno"), ("_Date", "date")]:
144
col = gtk.CheckMenuItem("Show " + label + " Column")
183
145
col.set_active(self.treeview.get_property(name + "-column-visible"))
184
146
col.connect('toggled', self._col_visibility_changed, name)
185
147
view_menu.add(col)
187
149
go_menu = gtk.Menu()
188
go_menu.set_accel_group(self.accel_group)
189
150
go_menuitem = gtk.MenuItem("_Go")
190
151
go_menuitem.set_submenu(go_menu)
192
go_menu_next = self.next_rev_action.create_menu_item()
193
go_menu_prev = self.prev_rev_action.create_menu_item()
195
tag_image = gtk.Image()
196
tag_image.set_from_file(icon_path("tag-16.png"))
197
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
198
self.go_menu_tags.set_image(tag_image)
201
go_menu.add(go_menu_next)
202
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)
203
171
go_menu.add(gtk.SeparatorMenuItem())
204
go_menu.add(self.go_menu_tags)
172
go_menu.add(go_menu_tags)
206
174
revision_menu = gtk.Menu()
207
175
revision_menuitem = gtk.MenuItem("_Revision")
210
178
revision_menu_diff = gtk.MenuItem("View Changes")
211
179
revision_menu_diff.connect('activate',
214
revision_menu_compare = gtk.MenuItem("Compare with...")
215
revision_menu_compare.connect('activate',
216
self._compare_with_cb)
180
lambda w: self.treeview.show_diff())
218
182
revision_menu_tag = gtk.MenuItem("Tag Revision")
219
183
revision_menu_tag.connect('activate', self._tag_revision_cb)
221
185
revision_menu.add(revision_menu_tag)
222
186
revision_menu.add(revision_menu_diff)
223
revision_menu.add(revision_menu_compare)
225
188
branch_menu = gtk.Menu()
226
189
branch_menuitem = gtk.MenuItem("_Branch")
229
192
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
230
193
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
233
from bzrlib.plugins import search
235
mutter("Didn't find search plugin")
237
branch_index_menuitem = gtk.MenuItem("_Index")
238
branch_index_menuitem.connect('activate', self._branch_index_cb)
239
branch_menu.add(branch_index_menuitem)
241
195
help_menu = gtk.Menu()
242
196
help_menuitem = gtk.MenuItem("_Help")
243
197
help_menuitem.set_submenu(help_menu)
245
help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
246
help_about_menuitem.connect('activate', self._about_dialog_cb)
248
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)
250
204
menubar.add(file_menuitem)
251
205
menubar.add(edit_menuitem)
252
206
menubar.add(view_menuitem)
257
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
261
233
def construct_top(self):
262
234
"""Construct the top-half of the window."""
263
235
# FIXME: Make broken_line_length configurable
265
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
267
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",
268
244
self._treeselection_changed_cb)
269
self.treeview.connect('revision-activated',
270
self._tree_revision_activated)
272
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
274
for col in ["revno", "date"]:
275
option = self.config.get_user_option(col + '-column-visible')
276
if option is not None:
277
self.treeview.set_property(col + '-column-visible', option == 'True')
279
self.treeview.set_property(col + '-column-visible', False)
246
self.treeview.connect('revisions-loaded',
247
lambda x: self.loading_msg_box.hide())
281
249
self.treeview.show()
283
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
284
align.set_padding(5, 0, 0, 0)
285
align.add(self.treeview)
290
253
def construct_navigation(self):
291
254
"""Construct the navigation buttons."""
292
255
self.toolbar = gtk.Toolbar()
293
256
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
295
self.prev_button = self.prev_rev_action.create_tool_item()
296
self.toolbar.insert(self.prev_button, -1)
298
self.next_button = self.next_rev_action.create_tool_item()
299
self.toolbar.insert(self.next_button, -1)
301
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
303
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
304
refresh_button.connect('clicked', self._refresh_clicked)
305
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)
307
270
self.toolbar.show_all()
311
274
def construct_bottom(self):
312
275
"""Construct the bottom half of the window."""
313
276
from bzrlib.plugins.gtk.revisionview import RevisionView
314
self.revisionview = RevisionView(branch=self.branch)
277
self.revisionview = RevisionView(None, tags=[], show_children=True, branch=self.branch)
315
278
(width, height) = self.get_size()
316
279
self.revisionview.set_size_request(width, int(height / 2.5))
317
280
self.revisionview.show()
318
281
self.revisionview.set_show_callback(self._show_clicked_cb)
319
self.revisionview.connect('notify::revision', self._go_clicked_cb)
320
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
282
self.revisionview.set_go_callback(self._go_clicked_cb)
321
283
return self.revisionview
323
285
def _tag_selected_cb(self, menuitem, revid):
329
291
parents = self.treeview.get_parents()
330
292
children = self.treeview.get_children()
332
if revision and revision != NULL_REVISION:
333
prev_menu = gtk.Menu()
294
if revision is not None:
295
back_menu = gtk.Menu()
334
296
if len(parents) > 0:
335
self.prev_rev_action.set_sensitive(True)
297
self.back_button.set_sensitive(True)
336
298
for parent_id in parents:
337
if parent_id and parent_id != NULL_REVISION:
338
parent = self.branch.repository.get_revision(parent_id)
340
str = ' (' + parent.properties['branch-nick'] + ')'
299
parent = self.branch.repository.get_revision(parent_id)
301
str = ' (' + parent.properties['branch-nick'] + ')'
344
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
345
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)
349
self.prev_rev_action.set_sensitive(False)
352
self.prev_button.set_menu(prev_menu)
354
next_menu = gtk.Menu()
310
self.back_button.set_sensitive(False)
313
self.back_button.set_menu(back_menu)
315
fwd_menu = gtk.Menu()
355
316
if len(children) > 0:
356
self.next_rev_action.set_sensitive(True)
317
self.fwd_button.set_sensitive(True)
357
318
for child_id in children:
358
319
child = self.branch.repository.get_revision(child_id)
364
325
item = gtk.MenuItem(child.message.split("\n")[0] + str)
365
326
item.connect('activate', self._set_revision_cb, child_id)
369
self.next_rev_action.set_sensitive(False)
372
self.next_button.set_menu(next_menu)
374
self.revisionview.set_revision(revision)
375
self.revisionview.set_children(children)
377
def _tree_revision_activated(self, widget, path, col):
378
# TODO: more than one parent
379
"""Callback for when a treeview row gets activated."""
380
revision = self.treeview.get_revision()
381
parents = self.treeview.get_parents()
383
if len(parents) == 0:
386
parent_id = parents[0]
388
self.show_diff(revision.revision_id, parent_id)
389
self.treeview.grab_focus()
391
def _menu_diff_cb(self,w):
392
(path, focus) = self.treeview.treeview.get_cursor()
393
revid = self.treeview.model[path][treemodel.REVID]
395
parentids = self.branch.repository.revision_parents(revid)
397
if len(parentids) == 0:
398
parentid = NULL_REVISION
400
parentid = parentids[0]
402
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)
404
342
def _back_clicked_cb(self, *args):
405
343
"""Callback for when the back button is clicked."""
409
347
"""Callback for when the forward button is clicked."""
410
348
self.treeview.forward()
412
def _go_clicked_cb(self, w, p):
350
def _go_clicked_cb(self, revid):
413
351
"""Callback for when the go button for a parent is clicked."""
414
if self.revisionview.get_revision() is not None:
415
self.treeview.set_revision(self.revisionview.get_revision())
352
self.treeview.set_revision_id(revid)
417
354
def _show_clicked_cb(self, revid, parentid):
418
355
"""Callback for when the show button for a parent is clicked."""
419
self.show_diff(revid, parentid)
356
self.treeview.show_diff(revid, parentid)
420
357
self.treeview.grab_focus()
422
def _compare_with_cb(self,w):
423
"""Callback for revision 'compare with' menu. Will show a small
424
dialog with branch revisions to compare with selected revision in TreeView"""
426
from bzrlib.plugins.gtk.revbrowser import RevisionBrowser
428
rb = RevisionBrowser(self.branch,self)
431
if ret == gtk.RESPONSE_OK:
432
(path, focus) = self.treeview.treeview.get_cursor()
433
revid = self.treeview.model[path][treemodel.REVID]
434
self.show_diff(revid, rb.selected_revid)
438
359
def _set_revision_cb(self, w, revision_id):
439
360
self.treeview.set_revision_id(revision_id)
449
370
self.config.set_user_option('viz-compact-view', option)
450
self.treeview.set_property('compact', self.compact_view)
451
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)
453
379
def _tag_revision_cb(self, w):
455
self.treeview.set_sensitive(False)
456
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
457
response = dialog.run()
458
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)
461
if response == gtk.RESPONSE_OK:
462
self.treeview.add_tag(dialog.tagname, dialog._revid)
467
self.treeview.set_sensitive(True)
469
def _branch_index_cb(self, w):
470
from bzrlib.plugins.search import index as _mod_index
471
_mod_index.index_url(self.branch.base)
473
def _about_dialog_cb(self, w):
474
from bzrlib.plugins.gtk.about import AboutDialog
478
394
def _col_visibility_changed(self, col, property):
479
self.config.set_user_option(property + '-column-visible', col.get_active())
480
395
self.treeview.set_property(property + '-column-visible', col.get_active())
482
397
def _toolbar_visibility_changed(self, col):
489
404
dialog = AboutDialog()
490
405
dialog.connect('response', lambda d,r: d.destroy())
493
def _refresh_clicked(self, w):
494
self.treeview.refresh()
496
def _update_tags(self):
499
if self.branch.supports_tags():
500
tags = self.branch.tags.get_tag_dict().items()
503
for tag, revid in tags:
504
tag_image = gtk.Image()
505
tag_image.set_from_file(icon_path('tag-16.png'))
506
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
507
tag_item.set_image(tag_image)
508
tag_item.connect('activate', self._tag_selected_cb, revid)
510
self.go_menu_tags.set_submenu(menu)
512
self.go_menu_tags.set_sensitive(len(tags) != 0)
514
self.go_menu_tags.set_sensitive(False)
516
self.go_menu_tags.show_all()
518
def show_diff(self, revid=None, parentid=None):
519
"""Open a new window to show a diff between the given revisions."""
520
from bzrlib.plugins.gtk.diff import DiffWindow
521
window = DiffWindow(parent=self)
524
parentid = NULL_REVISION
526
rev_tree = self.branch.repository.revision_tree(revid)
527
parent_tree = self.branch.repository.revision_tree(parentid)
529
description = revid + " - " + self.branch.nick
530
window.set_diff(description, rev_tree, parent_tree)