16
16
from bzrlib.plugins.gtk.window import Window
17
from bzrlib.plugins.gtk import icon_path
17
18
from bzrlib.plugins.gtk.tags import AddTagDialog
18
19
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
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
25
26
class BranchWindow(Window):
29
30
for a particular branch.
32
def __init__(self, branch, start, maxnum, parent=None):
33
def __init__(self, branch, start_revs, maxnum, parent=None):
33
34
"""Create a new BranchWindow.
35
36
:param branch: Branch object for branch to show.
36
:param start: Revision id of top revision.
37
:param start_revs: Revision ids of top revisions.
37
38
:param maxnum: Maximum number of revisions to display,
67
68
gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
68
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)
70
72
self.accel_group = gtk.AccelGroup()
71
73
self.add_accel_group(self.accel_group)
84
86
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
85
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()
89
97
def set_revision(self, revid):
102
110
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
103
111
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
104
vbox.pack_start(self.construct_loading_msg(), expand=False, fill=True)
106
113
vbox.pack_start(self.paned, expand=True, fill=True)
107
114
vbox.set_focus_child(self.paned)
121
128
file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
122
129
file_menu_quit.connect('activate', lambda x: gtk.main_quit())
124
file_menu.add(file_menu_close)
131
if self._parent is not None:
132
file_menu.add(file_menu_close)
125
133
file_menu.add(file_menu_quit)
127
135
edit_menu = gtk.Menu()
133
141
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
134
142
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())
144
edit_menu_globopts = gtk.MenuItem("Global Settings")
145
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
139
147
edit_menu.add(edit_menu_find)
140
148
edit_menu.add(edit_menu_branchopts)
141
edit_menu.add(edit_menu_prefs)
149
edit_menu.add(edit_menu_globopts)
143
151
view_menu = gtk.Menu()
144
152
view_menuitem = gtk.MenuItem("_View")
145
153
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())
147
161
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
148
162
view_menu_toolbar.set_active(True)
149
163
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
156
170
view_menu.add(view_menu_compact)
157
171
view_menu.add(gtk.SeparatorMenuItem())
159
for (label, name) in [("Revision _Number", "revno"), ("_Date", "date")]:
160
col = gtk.CheckMenuItem("Show " + label + " Column")
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")]:
161
183
col.set_active(self.treeview.get_property(name + "-column-visible"))
162
184
col.connect('toggled', self._col_visibility_changed, name)
163
185
view_menu.add(col)
170
192
go_menu_next = self.next_rev_action.create_menu_item()
171
193
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)
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)
190
201
go_menu.add(go_menu_next)
191
202
go_menu.add(go_menu_prev)
192
203
go_menu.add(gtk.SeparatorMenuItem())
193
go_menu.add(go_menu_tags)
204
go_menu.add(self.go_menu_tags)
195
206
revision_menu = gtk.Menu()
196
207
revision_menuitem = gtk.MenuItem("_Revision")
199
210
revision_menu_diff = gtk.MenuItem("View Changes")
200
211
revision_menu_diff.connect('activate',
201
lambda w: self.treeview.show_diff())
214
revision_menu_compare = gtk.MenuItem("Compare with...")
215
revision_menu_compare.connect('activate',
216
self._compare_with_cb)
203
218
revision_menu_tag = gtk.MenuItem("Tag Revision")
204
219
revision_menu_tag.connect('activate', self._tag_revision_cb)
206
221
revision_menu.add(revision_menu_tag)
207
222
revision_menu.add(revision_menu_diff)
223
revision_menu.add(revision_menu_compare)
209
225
branch_menu = gtk.Menu()
210
226
branch_menuitem = gtk.MenuItem("_Branch")
213
229
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
214
230
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)
216
241
help_menu = gtk.Menu()
217
242
help_menuitem = gtk.MenuItem("_Help")
218
243
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)
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)
225
250
menubar.add(file_menuitem)
226
251
menubar.add(edit_menuitem)
227
252
menubar.add(view_menuitem)
232
257
menubar.show_all()
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
261
def construct_top(self):
255
262
"""Construct the top-half of the window."""
256
263
# FIXME: Make broken_line_length configurable
257
if self.compact_view:
262
self.treeview = TreeView(self.branch, self.start, self.maxnum, brokenlines)
265
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
264
267
self.treeview.connect('revision-selected',
265
268
self._treeselection_changed_cb)
269
self.treeview.connect('revision-activated',
270
self._tree_revision_activated)
267
self.treeview.connect('revisions-loaded',
268
lambda x: self.loading_msg_box.hide())
272
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
270
274
for col in ["revno", "date"]:
271
275
option = self.config.get_user_option(col + '-column-visible')
272
276
if option is not None:
273
277
self.treeview.set_property(col + '-column-visible', option == 'True')
279
self.treeview.set_property(col + '-column-visible', False)
275
281
self.treeview.show()
292
298
self.next_button = self.next_rev_action.create_tool_item()
293
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)
295
307
self.toolbar.show_all()
297
309
return self.toolbar
299
311
def construct_bottom(self):
300
312
"""Construct the bottom half of the window."""
301
313
from bzrlib.plugins.gtk.revisionview import RevisionView
302
self.revisionview = RevisionView(None, tags=[], show_children=True, branch=self.branch)
314
self.revisionview = RevisionView(branch=self.branch)
303
315
(width, height) = self.get_size()
304
316
self.revisionview.set_size_request(width, int(height / 2.5))
305
317
self.revisionview.show()
306
318
self.revisionview.set_show_callback(self._show_clicked_cb)
307
self.revisionview.set_go_callback(self._go_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())
308
321
return self.revisionview
310
323
def _tag_selected_cb(self, menuitem, revid):
316
329
parents = self.treeview.get_parents()
317
330
children = self.treeview.get_children()
319
if revision is not None:
332
if revision and revision != NULL_REVISION:
320
333
prev_menu = gtk.Menu()
321
334
if len(parents) > 0:
322
335
self.prev_rev_action.set_sensitive(True)
323
336
for parent_id in parents:
324
parent = self.branch.repository.get_revision(parent_id)
326
str = ' (' + parent.properties['branch-nick'] + ')'
337
if parent_id and parent_id != NULL_REVISION:
338
parent = self.branch.repository.get_revision(parent_id)
340
str = ' (' + parent.properties['branch-nick'] + ')'
330
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
331
item.connect('activate', self._set_revision_cb, parent_id)
344
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
345
item.connect('activate', self._set_revision_cb, parent_id)
333
347
prev_menu.show_all()
335
349
self.prev_rev_action.set_sensitive(False)
358
372
self.next_button.set_menu(next_menu)
361
if self.branch.supports_tags():
362
tagdict = self.branch.tags.get_reverse_tag_dict()
363
if tagdict.has_key(revision.revision_id):
364
tags = tagdict[revision.revision_id]
365
self.revisionview.set_revision(revision, tags, children)
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)
367
404
def _back_clicked_cb(self, *args):
368
405
"""Callback for when the back button is clicked."""
372
409
"""Callback for when the forward button is clicked."""
373
410
self.treeview.forward()
375
def _go_clicked_cb(self, revid):
412
def _go_clicked_cb(self, w, p):
376
413
"""Callback for when the go button for a parent is clicked."""
377
self.treeview.set_revision_id(revid)
414
if self.revisionview.get_revision() is not None:
415
self.treeview.set_revision(self.revisionview.get_revision())
379
417
def _show_clicked_cb(self, revid, parentid):
380
418
"""Callback for when the show button for a parent is clicked."""
381
self.treeview.show_diff(revid, parentid)
419
self.show_diff(revid, parentid)
382
420
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)
384
438
def _set_revision_cb(self, w, revision_id):
385
439
self.treeview.set_revision_id(revision_id)
395
449
self.config.set_user_option('viz-compact-view', option)
397
revision = self.treeview.get_revision()
399
self.paned.get_child1().destroy()
400
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
402
gobject.idle_add(self.set_revision, revision.revision_id)
450
self.treeview.set_property('compact', self.compact_view)
451
self.treeview.refresh()
404
453
def _tag_revision_cb(self, w):
406
455
self.treeview.set_sensitive(False)
408
456
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
409
457
response = dialog.run()
410
458
if response != gtk.RESPONSE_NONE:
413
461
if response == gtk.RESPONSE_OK:
415
self.branch.lock_write()
416
self.branch.tags.set_tag(dialog.tagname, dialog._revid)
462
self.treeview.add_tag(dialog.tagname, dialog._revid)
423
self.branch.lock_read()
424
self.treeview.emit("revision-selected")
425
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
427
478
def _col_visibility_changed(self, col, property):
428
479
self.config.set_user_option(property + '-column-visible', col.get_active())
429
480
self.treeview.set_property(property + '-column-visible', col.get_active())
438
489
dialog = AboutDialog()
439
490
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)