16
from bzrlib.plugins.gtk.window import Window
16
17
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.revisionmenu import RevisionMenu
21
from bzrlib.plugins.gtk.window import Window
20
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
23
22
from bzrlib.config import BranchConfig, GlobalConfig
24
23
from bzrlib.revision import Revision, NULL_REVISION
54
53
self.compact_view = False
56
self.set_title(branch._get_nick(local=True) + " - revision history")
55
self.set_title(branch.nick + " - revision history")
58
# user-configured window size
59
size = self._load_size('viz-window-size')
63
# Use three-quarters of the screen by default
64
screen = self.get_screen()
65
monitor = screen.get_monitor_geometry(0)
66
width = int(monitor.width * 0.75)
67
height = int(monitor.height * 0.75)
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)
68
62
self.set_default_size(width, height)
69
self.set_size_request(width/3, height/3)
70
self._save_size_on_destroy(self, 'viz-window-size')
73
65
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
105
def _save_size_on_destroy(self, widget, config_name):
106
"""Creates a hook that saves the size of widget to config option
107
config_name when the window is destroyed/closed."""
109
width, height = widget.allocation.width, widget.allocation.height
110
value = '%sx%s' % (width, height)
111
self.config.set_user_option(config_name, value)
112
self.connect("destroy", save_size)
114
97
def set_revision(self, revid):
115
98
self.treeview.set_revision_id(revid)
122
105
self.paned = gtk.VPaned()
123
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
124
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
106
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
107
self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
125
108
self.paned.show()
127
nav = self.construct_navigation()
128
menubar = self.construct_menubar()
129
vbox.pack_start(menubar, expand=False, fill=True)
130
vbox.pack_start(nav, expand=False, fill=True)
110
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
111
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
132
113
vbox.pack_start(self.paned, expand=True, fill=True)
133
114
vbox.set_focus_child(self.paned)
135
self.treeview.connect('revision-selected',
136
self._treeselection_changed_cb)
137
self.treeview.connect('revision-activated',
138
self._tree_revision_activated)
140
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
143
118
def construct_menubar(self):
144
119
menubar = gtk.MenuBar()
161
136
edit_menuitem = gtk.MenuItem("_Edit")
162
137
edit_menuitem.set_submenu(edit_menu)
139
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
164
141
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
165
142
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
167
144
edit_menu_globopts = gtk.MenuItem("Global Settings")
168
145
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
147
edit_menu.add(edit_menu_find)
170
148
edit_menu.add(edit_menu_branchopts)
171
149
edit_menu.add(edit_menu_globopts)
183
161
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
184
162
view_menu_toolbar.set_active(True)
185
if self.config.get_user_option('viz-toolbar-visible') == 'False':
186
view_menu_toolbar.set_active(False)
188
163
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
190
165
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
191
166
view_menu_compact.set_active(self.compact_view)
192
167
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
194
view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
195
view_menu_diffs.set_active(False)
196
if self.config.get_user_option('viz-show-diffs') == 'True':
197
view_menu_diffs.set_active(True)
198
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
200
view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
201
view_menu_wide_diffs.set_active(False)
202
if self.config.get_user_option('viz-wide-diffs') == 'True':
203
view_menu_wide_diffs.set_active(True)
204
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
206
view_menu_wrap_diffs = gtk.CheckMenuItem("Wrap _Long Lines in Diffs")
207
view_menu_wrap_diffs.set_active(False)
208
if self.config.get_user_option('viz-wrap-diffs') == 'True':
209
view_menu_wrap_diffs.set_active(True)
210
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
212
169
view_menu.add(view_menu_toolbar)
213
170
view_menu.add(view_menu_compact)
214
171
view_menu.add(gtk.SeparatorMenuItem())
215
view_menu.add(view_menu_diffs)
216
view_menu.add(view_menu_wide_diffs)
217
view_menu.add(view_menu_wrap_diffs)
218
view_menu.add(gtk.SeparatorMenuItem())
220
173
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
221
174
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
243
196
tag_image.set_from_file(icon_path("tag-16.png"))
244
197
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
245
198
self.go_menu_tags.set_image(tag_image)
246
self.treeview.connect('refreshed', lambda w: self._update_tags())
248
201
go_menu.add(go_menu_next)
249
202
go_menu.add(go_menu_prev)
250
203
go_menu.add(gtk.SeparatorMenuItem())
251
204
go_menu.add(self.go_menu_tags)
253
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
206
revision_menu = gtk.Menu()
254
207
revision_menuitem = gtk.MenuItem("_Revision")
255
revision_menuitem.set_submenu(self.revision_menu)
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)
257
225
branch_menu = gtk.Menu()
258
226
branch_menuitem = gtk.MenuItem("_Branch")
266
234
except ImportError:
267
235
mutter("Didn't find search plugin")
269
branch_menu.add(gtk.SeparatorMenuItem())
271
237
branch_index_menuitem = gtk.MenuItem("_Index")
272
238
branch_index_menuitem.connect('activate', self._branch_index_cb)
273
239
branch_menu.add(branch_index_menuitem)
275
branch_search_menuitem = gtk.MenuItem("_Search")
276
branch_search_menuitem.connect('activate', self._branch_search_cb)
277
branch_menu.add(branch_search_menuitem)
279
241
help_menu = gtk.Menu()
280
242
help_menuitem = gtk.MenuItem("_Help")
281
243
help_menuitem.set_submenu(help_menu)
303
265
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())
305
274
for col in ["revno", "date"]:
306
275
option = self.config.get_user_option(col + '-column-visible')
307
276
if option is not None:
314
283
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
315
284
align.set_padding(5, 0, 0, 0)
316
285
align.add(self.treeview)
317
# user-configured size
318
size = self._load_size('viz-graph-size')
321
align.set_size_request(width, height)
323
(width, height) = self.get_size()
324
align.set_size_request(width, int(height / 2.5))
325
self._save_size_on_destroy(align, 'viz-graph-size')
351
311
def construct_bottom(self):
352
312
"""Construct the bottom half of the window."""
353
if self.config.get_user_option('viz-wide-diffs') == 'True':
354
self.diff_paned = gtk.VPaned()
356
self.diff_paned = gtk.HPaned()
357
(width, height) = self.get_size()
358
self.diff_paned.set_size_request(20, 20) # shrinkable
360
313
from bzrlib.plugins.gtk.revisionview import RevisionView
361
314
self.revisionview = RevisionView(branch=self.branch)
362
self.revisionview.set_size_request(width/3, int(height / 2.5))
363
# user-configured size
364
size = self._load_size('viz-revisionview-size')
367
self.revisionview.set_size_request(width, height)
368
self._save_size_on_destroy(self.revisionview, 'viz-revisionview-size')
315
(width, height) = self.get_size()
316
self.revisionview.set_size_request(width, int(height / 2.5))
369
317
self.revisionview.show()
370
318
self.revisionview.set_show_callback(self._show_clicked_cb)
371
319
self.revisionview.connect('notify::revision', self._go_clicked_cb)
372
320
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
373
self.diff_paned.pack1(self.revisionview)
375
from bzrlib.plugins.gtk.diff import DiffWidget
376
self.diff = DiffWidget()
377
self.diff_paned.pack2(self.diff)
379
self.diff_paned.show_all()
380
if self.config.get_user_option('viz-show-diffs') != 'True':
383
return self.diff_paned
321
return self.revisionview
385
323
def _tag_selected_cb(self, menuitem, revid):
386
324
self.treeview.set_revision_id(revid)
438
374
self.revisionview.set_revision(revision)
439
375
self.revisionview.set_children(children)
440
self.update_diff_panel(revision, parents)
442
377
def _tree_revision_activated(self, widget, path, col):
443
378
# TODO: more than one parent
444
379
"""Callback for when a treeview row gets activated."""
446
381
parents = self.treeview.get_parents()
448
383
if len(parents) == 0:
449
parent_id = NULL_REVISION
451
386
parent_id = parents[0]
453
388
self.show_diff(revision.revision_id, parent_id)
454
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)
456
404
def _back_clicked_cb(self, *args):
457
405
"""Callback for when the back button is clicked."""
458
406
self.treeview.back()
471
419
self.show_diff(revid, parentid)
472
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)
474
438
def _set_revision_cb(self, w, revision_id):
475
439
self.treeview.set_revision_id(revision_id)
486
450
self.treeview.set_property('compact', self.compact_view)
487
451
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)
489
469
def _branch_index_cb(self, w):
490
470
from bzrlib.plugins.search import index as _mod_index
491
471
_mod_index.index_url(self.branch.base)
493
def _branch_search_cb(self, w):
494
from bzrlib.plugins.search import index as _mod_index
495
from bzrlib.plugins.gtk.search import SearchDialog
496
from bzrlib.plugins.search import errors as search_errors
499
index = _mod_index.open_index_url(self.branch.base)
500
except search_errors.NoSearchIndex:
501
dialog = gtk.MessageDialog(self, type=gtk.MESSAGE_QUESTION,
502
buttons=gtk.BUTTONS_OK_CANCEL,
503
message_format="This branch has not been indexed yet. "
505
if dialog.run() == gtk.RESPONSE_OK:
507
index = _mod_index.index_url(self.branch.base)
512
dialog = SearchDialog(index)
514
if dialog.run() == gtk.RESPONSE_OK:
515
self.set_revision(dialog.get_revision())
519
473
def _about_dialog_cb(self, w):
520
474
from bzrlib.plugins.gtk.about import AboutDialog
528
482
def _toolbar_visibility_changed(self, col):
529
483
if col.get_active():
532
486
self.toolbar.hide()
533
self.config.set_user_option('viz-toolbar-visible', col.get_active())
535
def _make_diff_nonzero_size(self):
536
"""make sure the diff isn't zero-width or zero-height"""
537
alloc = self.diff.get_allocation()
538
if (alloc.width < 10) or (alloc.height < 10):
539
width, height = self.get_size()
540
self.revisionview.set_size_request(width/3, int(height / 2.5))
542
def _diff_visibility_changed(self, col):
543
"""Hide or show the diff panel."""
546
self._make_diff_nonzero_size()
549
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
550
self.update_diff_panel()
552
def _diff_placement_changed(self, col):
553
"""Toggle the diff panel's position."""
554
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
556
old = self.paned.get_child2()
557
self.paned.remove(old)
558
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
559
self._make_diff_nonzero_size()
561
self.treeview.emit('revision-selected')
563
def _diff_wrap_changed(self, widget):
564
"""Toggle word wrap in the diff widget."""
565
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
566
self.diff._on_wraplines_toggled(widget)
568
488
def _show_about_cb(self, w):
569
489
dialog = AboutDialog()
570
490
dialog.connect('response', lambda d,r: d.destroy())
579
499
if self.branch.supports_tags():
580
500
tags = self.branch.tags.get_tag_dict().items()
581
tags.sort(reverse=True)
582
503
for tag, revid in tags:
583
504
tag_image = gtk.Image()
584
505
tag_image.set_from_file(icon_path('tag-16.png'))
585
506
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
586
507
tag_item.set_image(tag_image)
587
508
tag_item.connect('activate', self._tag_selected_cb, revid)
588
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
589
509
menu.add(tag_item)
590
510
self.go_menu_tags.set_submenu(menu)
596
516
self.go_menu_tags.show_all()
598
def _load_size(self, name):
599
"""Read and parse 'name' from self.config.
600
The value is a string, formatted as WIDTHxHEIGHT
601
Returns None, or (width, height)
603
size = self.config.get_user_option(name)
605
width, height = [int(num) for num in size.split('x')]
606
# avoid writing config every time we start
610
def show_diff(self, revid=None, parentid=NULL_REVISION):
518
def show_diff(self, revid=None, parentid=None):
611
519
"""Open a new window to show a diff between the given revisions."""
612
520
from bzrlib.plugins.gtk.diff import DiffWindow
613
521
window = DiffWindow(parent=self)
524
parentid = NULL_REVISION
615
526
rev_tree = self.branch.repository.revision_tree(revid)
616
527
parent_tree = self.branch.repository.revision_tree(parentid)
618
description = revid + " - " + self.branch._get_nick(local=True)
529
description = revid + " - " + self.branch.nick
619
530
window.set_diff(description, rev_tree, parent_tree)
622
def update_diff_panel(self, revision=None, parents=None):
623
"""Show the current revision in the diff panel."""
624
if self.config.get_user_option('viz-show-diffs') != 'True':
627
if not revision: # default to selected row
628
revision = self.treeview.get_revision()
629
if revision == NULL_REVISION:
632
if not parents: # default to selected row's parents
633
parents = self.treeview.get_parents()
634
if len(parents) == 0:
635
parent_id = NULL_REVISION
637
parent_id = parents[0]
639
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
640
parent_tree = self.branch.repository.revision_tree(parent_id)
642
self.diff.set_diff(rev_tree, parent_tree)
643
if self.config.get_user_option('viz-wrap-diffs') == 'True':
644
self.diff._on_wraplines_toggled(wrap=True)