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
23
from bzrlib.config import BranchConfig, GlobalConfig
21
24
from bzrlib.revision import Revision, NULL_REVISION
22
from bzrlib.config import BranchConfig
23
from bzrlib.config import GlobalConfig
25
from bzrlib.trace import mutter
25
27
class BranchWindow(Window):
52
54
self.compact_view = False
54
self.set_title(branch.nick + " - revision history")
56
self.set_title(branch._get_nick(local=True) + " - revision history")
56
# Use three-quarters of the screen by default
57
screen = self.get_screen()
58
monitor = screen.get_monitor_geometry(0)
59
width = int(monitor.width * 0.75)
60
height = int(monitor.height * 0.75)
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)
61
68
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')
64
73
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)
96
114
def set_revision(self, revid):
97
115
self.treeview.set_revision_id(revid)
104
122
self.paned = gtk.VPaned()
105
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
106
self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
123
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
124
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
107
125
self.paned.show()
109
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
110
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
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)
112
132
vbox.pack_start(self.paned, expand=True, fill=True)
113
133
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())
117
143
def construct_menubar(self):
118
144
menubar = gtk.MenuBar()
135
161
edit_menuitem = gtk.MenuItem("_Edit")
136
162
edit_menuitem.set_submenu(edit_menu)
138
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
140
164
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
141
165
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
143
167
edit_menu_globopts = gtk.MenuItem("Global Settings")
144
168
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
146
edit_menu.add(edit_menu_find)
147
170
edit_menu.add(edit_menu_branchopts)
148
171
edit_menu.add(edit_menu_globopts)
160
183
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
161
184
view_menu_toolbar.set_active(True)
185
if self.config.get_user_option('viz-toolbar-visible') == 'False':
186
view_menu_toolbar.set_active(False)
162
188
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
164
190
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
165
191
view_menu_compact.set_active(self.compact_view)
166
192
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)
168
212
view_menu.add(view_menu_toolbar)
169
213
view_menu.add(view_menu_compact)
170
214
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())
172
220
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
173
221
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
195
243
tag_image.set_from_file(icon_path("tag-16.png"))
196
244
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
197
245
self.go_menu_tags.set_image(tag_image)
246
self.treeview.connect('refreshed', lambda w: self._update_tags())
200
248
go_menu.add(go_menu_next)
201
249
go_menu.add(go_menu_prev)
202
250
go_menu.add(gtk.SeparatorMenuItem())
203
251
go_menu.add(self.go_menu_tags)
205
revision_menu = gtk.Menu()
253
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
206
254
revision_menuitem = gtk.MenuItem("_Revision")
207
revision_menuitem.set_submenu(revision_menu)
209
revision_menu_diff = gtk.MenuItem("View Changes")
210
revision_menu_diff.connect('activate',
213
revision_menu_compare = gtk.MenuItem("Compare with...")
214
revision_menu_compare.connect('activate',
215
self._compare_with_cb)
217
revision_menu_tag = gtk.MenuItem("Tag Revision")
218
revision_menu_tag.connect('activate', self._tag_revision_cb)
220
revision_menu.add(revision_menu_tag)
221
revision_menu.add(revision_menu_diff)
222
revision_menu.add(revision_menu_compare)
255
revision_menuitem.set_submenu(self.revision_menu)
224
257
branch_menu = gtk.Menu()
225
258
branch_menuitem = gtk.MenuItem("_Branch")
228
261
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
229
262
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
265
from bzrlib.plugins import search
267
mutter("Didn't find search plugin")
269
branch_menu.add(gtk.SeparatorMenuItem())
271
branch_index_menuitem = gtk.MenuItem("_Index")
272
branch_index_menuitem.connect('activate', self._branch_index_cb)
273
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)
231
279
help_menu = gtk.Menu()
232
280
help_menuitem = gtk.MenuItem("_Help")
233
281
help_menuitem.set_submenu(help_menu)
255
303
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
257
self.treeview.connect('revision-selected',
258
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
305
for col in ["revno", "date"]:
265
306
option = self.config.get_user_option(col + '-column-visible')
266
307
if option is not None:
273
314
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
274
315
align.set_padding(5, 0, 0, 0)
275
316
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')
301
351
def construct_bottom(self):
302
352
"""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
303
360
from bzrlib.plugins.gtk.revisionview import RevisionView
304
361
self.revisionview = RevisionView(branch=self.branch)
305
(width, height) = self.get_size()
306
self.revisionview.set_size_request(width, int(height / 2.5))
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')
307
369
self.revisionview.show()
308
370
self.revisionview.set_show_callback(self._show_clicked_cb)
309
371
self.revisionview.connect('notify::revision', self._go_clicked_cb)
310
372
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
311
return self.revisionview
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
313
385
def _tag_selected_cb(self, menuitem, revid):
314
386
self.treeview.set_revision_id(revid)
364
438
self.revisionview.set_revision(revision)
365
439
self.revisionview.set_children(children)
440
self.update_diff_panel(revision, parents)
367
442
def _tree_revision_activated(self, widget, path, col):
368
443
# TODO: more than one parent
369
444
"""Callback for when a treeview row gets activated."""
371
446
parents = self.treeview.get_parents()
373
448
if len(parents) == 0:
449
parent_id = NULL_REVISION
376
451
parent_id = parents[0]
378
453
self.show_diff(revision.revision_id, parent_id)
379
454
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)
394
456
def _back_clicked_cb(self, *args):
395
457
"""Callback for when the back button is clicked."""
396
458
self.treeview.back()
409
471
self.show_diff(revid, parentid)
410
472
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
474
def _set_revision_cb(self, w, revision_id):
429
475
self.treeview.set_revision_id(revision_id)
440
486
self.treeview.set_property('compact', self.compact_view)
441
487
self.treeview.refresh()
443
def _tag_revision_cb(self, w):
489
def _branch_index_cb(self, w):
490
from bzrlib.plugins.search import index as _mod_index
491
_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
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:
451
if response == gtk.RESPONSE_OK:
452
self.treeview.add_tag(dialog.tagname, dialog._revid)
457
self.treeview.set_sensitive(True)
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())
459
519
def _about_dialog_cb(self, w):
460
520
from bzrlib.plugins.gtk.about import AboutDialog
468
528
def _toolbar_visibility_changed(self, col):
469
529
if col.get_active():
472
532
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)
474
568
def _show_about_cb(self, w):
475
569
dialog = AboutDialog()
476
570
dialog.connect('response', lambda d,r: d.destroy())
485
579
if self.branch.supports_tags():
486
580
tags = self.branch.tags.get_tag_dict().items()
581
tags.sort(reverse=True)
489
582
for tag, revid in tags:
490
583
tag_image = gtk.Image()
491
584
tag_image.set_from_file(icon_path('tag-16.png'))
492
585
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
493
586
tag_item.set_image(tag_image)
494
587
tag_item.connect('activate', self._tag_selected_cb, revid)
588
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
495
589
menu.add(tag_item)
496
590
self.go_menu_tags.set_submenu(menu)
502
596
self.go_menu_tags.show_all()
504
def show_diff(self, revid=None, parentid=None):
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):
505
611
"""Open a new window to show a diff between the given revisions."""
506
612
from bzrlib.plugins.gtk.diff import DiffWindow
507
613
window = DiffWindow(parent=self)
510
parentid = NULL_REVISION
512
615
rev_tree = self.branch.repository.revision_tree(revid)
513
616
parent_tree = self.branch.repository.revision_tree(parentid)
515
description = revid + " - " + self.branch.nick
618
description = revid + " - " + self.branch._get_nick(local=True)
516
619
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)