16
from bzrlib.plugins.gtk.window import Window
17
16
from bzrlib.plugins.gtk import icon_path
17
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
18
18
from bzrlib.plugins.gtk.tags import AddTagDialog
19
19
from bzrlib.plugins.gtk.preferences import PreferencesWindow
20
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
20
from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
21
from bzrlib.plugins.gtk.window import Window
22
23
from bzrlib.config import BranchConfig, GlobalConfig
23
24
from bzrlib.revision import Revision, NULL_REVISION
55
58
self.set_title(branch.nick + " - revision history")
57
# Use three-quarters of the screen by default
58
screen = self.get_screen()
59
monitor = screen.get_monitor_geometry(0)
60
width = int(monitor.width * 0.75)
61
height = int(monitor.height * 0.75)
60
# user-configured window size
61
size = self._load_size('viz-window-size')
65
# Use three-quarters of the screen by default
66
screen = self.get_screen()
67
monitor = screen.get_monitor_geometry(0)
68
width = int(monitor.width * 0.75)
69
height = int(monitor.height * 0.75)
62
70
self.set_default_size(width, height)
71
self.set_size_request(width/3, height/3)
72
self.connect("size-allocate", self._on_size_allocate, 'viz-window-size')
65
75
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
105
115
self.paned = gtk.VPaned()
106
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
107
self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
116
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
117
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
108
118
self.paned.show()
110
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
111
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
120
nav = self.construct_navigation()
121
menubar = self.construct_menubar()
122
vbox.pack_start(menubar, expand=False, fill=True)
123
vbox.pack_start(nav, expand=False, fill=True)
113
125
vbox.pack_start(self.paned, expand=True, fill=True)
114
126
vbox.set_focus_child(self.paned)
128
self.treeview.connect('revision-selected',
129
self._treeselection_changed_cb)
130
self.treeview.connect('revision-activated',
131
self._tree_revision_activated)
133
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
118
136
def construct_menubar(self):
119
137
menubar = gtk.MenuBar()
136
154
edit_menuitem = gtk.MenuItem("_Edit")
137
155
edit_menuitem.set_submenu(edit_menu)
139
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
141
157
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
142
158
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
144
160
edit_menu_globopts = gtk.MenuItem("Global Settings")
145
161
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
147
edit_menu.add(edit_menu_find)
148
163
edit_menu.add(edit_menu_branchopts)
149
164
edit_menu.add(edit_menu_globopts)
161
176
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
162
177
view_menu_toolbar.set_active(True)
178
if self.config.get_user_option('viz-toolbar-visible') == 'False':
179
view_menu_toolbar.set_active(False)
163
181
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
165
183
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
166
184
view_menu_compact.set_active(self.compact_view)
167
185
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
187
view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
188
view_menu_diffs.set_active(False)
189
if self.config.get_user_option('viz-show-diffs') == 'True':
190
view_menu_diffs.set_active(True)
191
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
193
view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
194
view_menu_wide_diffs.set_active(False)
195
if self.config.get_user_option('viz-wide-diffs') == 'True':
196
view_menu_wide_diffs.set_active(True)
197
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
199
view_menu_wrap_diffs = gtk.CheckMenuItem("Wrap _Long Lines in Diffs")
200
view_menu_wrap_diffs.set_active(False)
201
if self.config.get_user_option('viz-wrap-diffs') == 'True':
202
view_menu_wrap_diffs.set_active(True)
203
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
169
205
view_menu.add(view_menu_toolbar)
170
206
view_menu.add(view_menu_compact)
171
207
view_menu.add(gtk.SeparatorMenuItem())
208
view_menu.add(view_menu_diffs)
209
view_menu.add(view_menu_wide_diffs)
210
view_menu.add(view_menu_wrap_diffs)
211
view_menu.add(gtk.SeparatorMenuItem())
173
213
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
174
214
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
196
236
tag_image.set_from_file(icon_path("tag-16.png"))
197
237
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
198
238
self.go_menu_tags.set_image(tag_image)
239
self.treeview.connect('refreshed', lambda w: self._update_tags())
201
241
go_menu.add(go_menu_next)
202
242
go_menu.add(go_menu_prev)
203
243
go_menu.add(gtk.SeparatorMenuItem())
204
244
go_menu.add(self.go_menu_tags)
206
revision_menu = gtk.Menu()
246
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
207
247
revision_menuitem = gtk.MenuItem("_Revision")
208
revision_menuitem.set_submenu(revision_menu)
210
revision_menu_diff = gtk.MenuItem("View Changes")
211
revision_menu_diff.connect('activate',
214
revision_menu_compare = gtk.MenuItem("Compare with...")
215
revision_menu_compare.connect('activate',
216
self._compare_with_cb)
218
revision_menu_tag = gtk.MenuItem("Tag Revision")
219
revision_menu_tag.connect('activate', self._tag_revision_cb)
221
revision_menu.add(revision_menu_tag)
222
revision_menu.add(revision_menu_diff)
223
revision_menu.add(revision_menu_compare)
248
revision_menuitem.set_submenu(self.revision_menu)
225
250
branch_menu = gtk.Menu()
226
251
branch_menuitem = gtk.MenuItem("_Branch")
234
259
except ImportError:
235
260
mutter("Didn't find search plugin")
262
branch_menu.add(gtk.SeparatorMenuItem())
237
264
branch_index_menuitem = gtk.MenuItem("_Index")
238
265
branch_index_menuitem.connect('activate', self._branch_index_cb)
239
266
branch_menu.add(branch_index_menuitem)
268
branch_search_menuitem = gtk.MenuItem("_Search")
269
branch_search_menuitem.connect('activate', self._branch_search_cb)
270
branch_menu.add(branch_search_menuitem)
241
272
help_menu = gtk.Menu()
242
273
help_menuitem = gtk.MenuItem("_Help")
243
274
help_menuitem.set_submenu(help_menu)
265
296
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
267
self.treeview.connect('revision-selected',
268
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
298
for col in ["revno", "date"]:
275
299
option = self.config.get_user_option(col + '-column-visible')
276
300
if option is not None:
283
307
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
284
308
align.set_padding(5, 0, 0, 0)
285
309
align.add(self.treeview)
310
# user-configured size
311
size = self._load_size('viz-graph-size')
314
align.set_size_request(width, height)
316
(width, height) = self.get_size()
317
align.set_size_request(width, int(height / 2.5))
318
align.connect('size-allocate', self._on_size_allocate, 'viz-graph-size')
311
344
def construct_bottom(self):
312
345
"""Construct the bottom half of the window."""
346
if self.config.get_user_option('viz-wide-diffs') == 'True':
347
self.diff_paned = gtk.VPaned()
349
self.diff_paned = gtk.HPaned()
350
(width, height) = self.get_size()
351
self.diff_paned.set_size_request(20, 20) # shrinkable
313
353
from bzrlib.plugins.gtk.revisionview import RevisionView
314
354
self.revisionview = RevisionView(branch=self.branch)
315
(width, height) = self.get_size()
316
self.revisionview.set_size_request(width, int(height / 2.5))
355
self.revisionview.set_size_request(width/3, int(height / 2.5))
356
# user-configured size
357
size = self._load_size('viz-revisionview-size')
360
self.revisionview.set_size_request(width, height)
361
self.revisionview.connect('size-allocate', self._on_size_allocate, 'viz-revisionview-size')
317
362
self.revisionview.show()
318
363
self.revisionview.set_show_callback(self._show_clicked_cb)
319
364
self.revisionview.connect('notify::revision', self._go_clicked_cb)
320
365
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
321
return self.revisionview
366
self.diff_paned.pack1(self.revisionview)
368
from bzrlib.plugins.gtk.diff import DiffWidget
369
self.diff = DiffWidget()
370
self.diff_paned.pack2(self.diff)
372
self.diff_paned.show_all()
373
if self.config.get_user_option('viz-show-diffs') != 'True':
376
return self.diff_paned
323
378
def _tag_selected_cb(self, menuitem, revid):
324
379
self.treeview.set_revision_id(revid)
374
431
self.revisionview.set_revision(revision)
375
432
self.revisionview.set_children(children)
433
self.update_diff_panel(revision, parents)
377
435
def _tree_revision_activated(self, widget, path, col):
378
436
# TODO: more than one parent
379
437
"""Callback for when a treeview row gets activated."""
381
439
parents = self.treeview.get_parents()
383
441
if len(parents) == 0:
442
parent_id = NULL_REVISION
386
444
parent_id = parents[0]
388
446
self.show_diff(revision.revision_id, parent_id)
389
447
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)
404
449
def _back_clicked_cb(self, *args):
405
450
"""Callback for when the back button is clicked."""
406
451
self.treeview.back()
419
464
self.show_diff(revid, parentid)
420
465
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
467
def _set_revision_cb(self, w, revision_id):
439
468
self.treeview.set_revision_id(revision_id)
450
479
self.treeview.set_property('compact', self.compact_view)
451
480
self.treeview.refresh()
453
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:
461
if response == gtk.RESPONSE_OK:
462
self.treeview.add_tag(dialog.tagname, dialog._revid)
467
self.treeview.set_sensitive(True)
469
482
def _branch_index_cb(self, w):
470
483
from bzrlib.plugins.search import index as _mod_index
471
484
_mod_index.index_url(self.branch.base)
486
def _branch_search_cb(self, w):
487
from bzrlib.plugins.search import index as _mod_index
488
from bzrlib.plugins.gtk.search import SearchDialog
489
from bzrlib.plugins.search import errors as search_errors
492
index = _mod_index.open_index_url(self.branch.base)
493
except search_errors.NoSearchIndex:
494
dialog = gtk.MessageDialog(self, type=gtk.MESSAGE_QUESTION,
495
buttons=gtk.BUTTONS_OK_CANCEL,
496
message_format="This branch has not been indexed yet. "
498
if dialog.run() == gtk.RESPONSE_OK:
500
index = _mod_index.index_url(self.branch.base)
505
dialog = SearchDialog(index)
507
if dialog.run() == gtk.RESPONSE_OK:
508
self.set_revision(dialog.get_revision())
473
512
def _about_dialog_cb(self, w):
474
513
from bzrlib.plugins.gtk.about import AboutDialog
482
521
def _toolbar_visibility_changed(self, col):
483
522
if col.get_active():
486
525
self.toolbar.hide()
526
self.config.set_user_option('viz-toolbar-visible', col.get_active())
528
def _make_diff_nonzero_size(self):
529
"""make sure the diff isn't zero-width or zero-height"""
530
alloc = self.diff.get_allocation()
531
if (alloc.width < 10) or (alloc.height < 10):
532
width, height = self.get_size()
533
self.revisionview.set_size_request(width/3, int(height / 2.5))
535
def _diff_visibility_changed(self, col):
536
"""Hide or show the diff panel."""
539
self._make_diff_nonzero_size()
542
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
543
self.update_diff_panel()
545
def _diff_placement_changed(self, col):
546
"""Toggle the diff panel's position."""
547
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
549
old = self.paned.get_child2()
550
self.paned.remove(old)
551
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
552
self._make_diff_nonzero_size()
554
self.treeview.emit('revision-selected')
556
def _diff_wrap_changed(self, widget):
557
"""Toggle word wrap in the diff widget."""
558
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
559
self.diff._on_wraplines_toggled(widget)
488
561
def _show_about_cb(self, w):
489
562
dialog = AboutDialog()
490
563
dialog.connect('response', lambda d,r: d.destroy())
499
572
if self.branch.supports_tags():
500
573
tags = self.branch.tags.get_tag_dict().items()
574
tags.sort(reverse=True)
503
575
for tag, revid in tags:
504
576
tag_image = gtk.Image()
505
577
tag_image.set_from_file(icon_path('tag-16.png'))
506
578
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
507
579
tag_item.set_image(tag_image)
508
580
tag_item.connect('activate', self._tag_selected_cb, revid)
581
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
509
582
menu.add(tag_item)
510
583
self.go_menu_tags.set_submenu(menu)
516
589
self.go_menu_tags.show_all()
518
def show_diff(self, revid=None, parentid=None):
591
def _load_size(self, name):
592
"""Read and parse 'name' from self.config.
593
The value is a string, formatted as WIDTHxHEIGHT
594
Returns None, or (width, height)
596
size = self.config.get_user_option(name)
598
width, height = [int(num) for num in size.split('x')]
599
# avoid writing config every time we start
600
self._sizes[name] = (width, height)
604
def _on_size_allocate(self, widget, allocation, name):
605
"""When window has been resized, save the new size."""
607
if name in self._sizes:
608
width, height = self._sizes[name]
610
size_changed = (width != allocation.width) or \
611
(height != allocation.height)
614
width, height = allocation.width, allocation.height
615
self._sizes[name] = (width, height)
616
value = '%sx%s' % (width, height)
617
self.config.set_user_option(name, value)
619
def show_diff(self, revid=None, parentid=NULL_REVISION):
519
620
"""Open a new window to show a diff between the given revisions."""
520
621
from bzrlib.plugins.gtk.diff import DiffWindow
521
622
window = DiffWindow(parent=self)
524
parentid = NULL_REVISION
526
624
rev_tree = self.branch.repository.revision_tree(revid)
527
625
parent_tree = self.branch.repository.revision_tree(parentid)
530
628
window.set_diff(description, rev_tree, parent_tree)
631
def update_diff_panel(self, revision=None, parents=None):
632
"""Show the current revision in the diff panel."""
633
if self.config.get_user_option('viz-show-diffs') != 'True':
636
if not revision: # default to selected row
637
revision = self.treeview.get_revision()
638
if revision == NULL_REVISION:
641
if not parents: # default to selected row's parents
642
parents = self.treeview.get_parents()
643
if len(parents) == 0:
644
parent_id = NULL_REVISION
646
parent_id = parents[0]
648
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
649
parent_tree = self.branch.repository.revision_tree(parent_id)
651
self.diff.set_diff(rev_tree, parent_tree)
652
if self.config.get_user_option('viz-wrap-diffs') == 'True':
653
self.diff._on_wraplines_toggled(wrap=True)