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
23
from bzrlib.config import BranchConfig, GlobalConfig
20
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
24
21
from bzrlib.revision import Revision, NULL_REVISION
25
from bzrlib.trace import mutter
22
from bzrlib.config import BranchConfig
23
from bzrlib.config import GlobalConfig
27
25
class BranchWindow(Window):
58
54
self.set_title(branch.nick + " - revision history")
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)
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)
70
61
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')
75
64
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
115
104
self.paned = gtk.VPaned()
116
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
117
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
105
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
106
self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
118
107
self.paned.show()
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)
109
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
110
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
125
112
vbox.pack_start(self.paned, expand=True, fill=True)
126
113
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())
136
117
def construct_menubar(self):
137
118
menubar = gtk.MenuBar()
154
135
edit_menuitem = gtk.MenuItem("_Edit")
155
136
edit_menuitem.set_submenu(edit_menu)
138
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
157
140
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
158
141
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
160
143
edit_menu_globopts = gtk.MenuItem("Global Settings")
161
144
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
146
edit_menu.add(edit_menu_find)
163
147
edit_menu.add(edit_menu_branchopts)
164
148
edit_menu.add(edit_menu_globopts)
176
160
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
177
161
view_menu_toolbar.set_active(True)
178
if self.config.get_user_option('viz-toolbar-visible') == 'False':
179
view_menu_toolbar.set_active(False)
181
162
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
183
164
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
184
165
view_menu_compact.set_active(self.compact_view)
185
166
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)
205
168
view_menu.add(view_menu_toolbar)
206
169
view_menu.add(view_menu_compact)
207
170
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())
213
172
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
214
173
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
236
195
tag_image.set_from_file(icon_path("tag-16.png"))
237
196
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
238
197
self.go_menu_tags.set_image(tag_image)
239
self.treeview.connect('refreshed', lambda w: self._update_tags())
241
200
go_menu.add(go_menu_next)
242
201
go_menu.add(go_menu_prev)
243
202
go_menu.add(gtk.SeparatorMenuItem())
244
203
go_menu.add(self.go_menu_tags)
246
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
205
revision_menu = gtk.Menu()
247
206
revision_menuitem = gtk.MenuItem("_Revision")
248
revision_menuitem.set_submenu(self.revision_menu)
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)
250
224
branch_menu = gtk.Menu()
251
225
branch_menuitem = gtk.MenuItem("_Branch")
254
228
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
255
229
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
258
from bzrlib.plugins import search
260
mutter("Didn't find search plugin")
262
branch_menu.add(gtk.SeparatorMenuItem())
264
branch_index_menuitem = gtk.MenuItem("_Index")
265
branch_index_menuitem.connect('activate', self._branch_index_cb)
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)
272
231
help_menu = gtk.Menu()
273
232
help_menuitem = gtk.MenuItem("_Help")
274
233
help_menuitem.set_submenu(help_menu)
296
255
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())
298
264
for col in ["revno", "date"]:
299
265
option = self.config.get_user_option(col + '-column-visible')
300
266
if option is not None:
307
273
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
308
274
align.set_padding(5, 0, 0, 0)
309
275
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')
344
301
def construct_bottom(self):
345
302
"""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
353
303
from bzrlib.plugins.gtk.revisionview import RevisionView
354
304
self.revisionview = RevisionView(branch=self.branch)
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')
305
(width, height) = self.get_size()
306
self.revisionview.set_size_request(width, int(height / 2.5))
362
307
self.revisionview.show()
363
308
self.revisionview.set_show_callback(self._show_clicked_cb)
364
309
self.revisionview.connect('notify::revision', self._go_clicked_cb)
365
310
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
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
311
return self.revisionview
378
313
def _tag_selected_cb(self, menuitem, revid):
379
314
self.treeview.set_revision_id(revid)
431
364
self.revisionview.set_revision(revision)
432
365
self.revisionview.set_children(children)
433
self.update_diff_panel(revision, parents)
435
367
def _tree_revision_activated(self, widget, path, col):
436
368
# TODO: more than one parent
437
369
"""Callback for when a treeview row gets activated."""
439
371
parents = self.treeview.get_parents()
441
373
if len(parents) == 0:
442
parent_id = NULL_REVISION
444
376
parent_id = parents[0]
446
378
self.show_diff(revision.revision_id, parent_id)
447
379
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)
449
394
def _back_clicked_cb(self, *args):
450
395
"""Callback for when the back button is clicked."""
451
396
self.treeview.back()
464
409
self.show_diff(revid, parentid)
465
410
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)
467
428
def _set_revision_cb(self, w, revision_id):
468
429
self.treeview.set_revision_id(revision_id)
479
440
self.treeview.set_property('compact', self.compact_view)
480
441
self.treeview.refresh()
482
def _branch_index_cb(self, w):
483
from bzrlib.plugins.search import index as _mod_index
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
443
def _tag_revision_cb(self, w):
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())
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)
512
459
def _about_dialog_cb(self, w):
513
460
from bzrlib.plugins.gtk.about import AboutDialog
521
468
def _toolbar_visibility_changed(self, col):
522
469
if col.get_active():
525
472
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)
561
474
def _show_about_cb(self, w):
562
475
dialog = AboutDialog()
563
476
dialog.connect('response', lambda d,r: d.destroy())
572
485
if self.branch.supports_tags():
573
486
tags = self.branch.tags.get_tag_dict().items()
574
tags.sort(reverse=True)
575
489
for tag, revid in tags:
576
490
tag_image = gtk.Image()
577
491
tag_image.set_from_file(icon_path('tag-16.png'))
578
492
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
579
493
tag_item.set_image(tag_image)
580
494
tag_item.connect('activate', self._tag_selected_cb, revid)
581
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
582
495
menu.add(tag_item)
583
496
self.go_menu_tags.set_submenu(menu)
589
502
self.go_menu_tags.show_all()
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):
504
def show_diff(self, revid=None, parentid=None):
620
505
"""Open a new window to show a diff between the given revisions."""
621
506
from bzrlib.plugins.gtk.diff import DiffWindow
622
507
window = DiffWindow(parent=self)
510
parentid = NULL_REVISION
624
512
rev_tree = self.branch.repository.revision_tree(revid)
625
513
parent_tree = self.branch.repository.revision_tree(parentid)
628
516
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)