1
# -*- coding: UTF-8 -*-
3
4
This module contains the code to manage the branch information window,
4
5
which contains both the revision graph and details panes.
7
__copyright__ = "Copyright (c) 2005 Canonical Ltd."
8
__copyright__ = "Copyright © 2005 Canonical Ltd."
8
9
__author__ = "Scott James Remnant <scott@ubuntu.com>"
16
from bzrlib.plugins.gtk.window import Window
13
17
from bzrlib.plugins.gtk import icon_path
14
from bzrlib.plugins.gtk.branchview import TreeView
18
from bzrlib.plugins.gtk.tags import AddTagDialog
15
19
from bzrlib.plugins.gtk.preferences import PreferencesWindow
16
from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
17
from bzrlib.plugins.gtk.window import Window
20
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
21
from bzrlib.revision import Revision, NULL_REVISION
22
from bzrlib.config import BranchConfig
19
23
from bzrlib.config import GlobalConfig
20
from bzrlib.revision import NULL_REVISION
21
from bzrlib.trace import mutter
23
25
class BranchWindow(Window):
50
52
self.compact_view = False
52
self.set_title(branch._get_nick(local=True) + " - revision history")
54
self.set_title(branch.nick + " - revision history")
54
# user-configured window size
55
size = self._load_size('viz-window-size')
59
# Use three-quarters of the screen by default
60
screen = self.get_screen()
61
monitor = screen.get_monitor_geometry(0)
62
width = int(monitor.width * 0.75)
63
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)
64
61
self.set_default_size(width, height)
65
self.set_size_request(width/3, height/3)
66
self._save_size_on_destroy(self, 'viz-window-size')
69
64
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
76
71
self.accel_group = gtk.AccelGroup()
77
72
self.add_accel_group(self.accel_group)
79
if getattr(gtk.Action, 'set_tool_item_type', None) is not None:
80
# Not available before PyGtk-2.10
81
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
74
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
83
76
self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
84
77
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
103
def _save_size_on_destroy(self, widget, config_name):
104
"""Creates a hook that saves the size of widget to config option
105
config_name when the window is destroyed/closed."""
107
width, height = widget.allocation.width, widget.allocation.height
108
value = '%sx%s' % (width, height)
109
self.config.set_user_option(config_name, value)
110
self.connect("destroy", save_size)
112
96
def set_revision(self, revid):
113
97
self.treeview.set_revision_id(revid)
120
104
self.paned = gtk.VPaned()
121
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
122
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)
123
107
self.paned.show()
125
nav = self.construct_navigation()
126
menubar = self.construct_menubar()
127
vbox.pack_start(menubar, expand=False, fill=True)
128
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)
130
112
vbox.pack_start(self.paned, expand=True, fill=True)
131
113
vbox.set_focus_child(self.paned)
133
self.treeview.connect('revision-selected',
134
self._treeselection_changed_cb)
135
self.treeview.connect('revision-activated',
136
self._tree_revision_activated)
138
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
141
117
def construct_menubar(self):
148
124
file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
149
125
file_menu_close.connect('activate', lambda x: self.destroy())
151
127
file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
152
128
file_menu_quit.connect('activate', lambda x: gtk.main_quit())
154
130
if self._parent is not None:
155
131
file_menu.add(file_menu_close)
156
132
file_menu.add(file_menu_quit)
159
135
edit_menuitem = gtk.MenuItem("_Edit")
160
136
edit_menuitem.set_submenu(edit_menu)
138
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
162
140
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
163
141
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
165
143
edit_menu_globopts = gtk.MenuItem("Global Settings")
166
144
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
146
edit_menu.add(edit_menu_find)
168
147
edit_menu.add(edit_menu_branchopts)
169
148
edit_menu.add(edit_menu_globopts)
181
160
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
182
161
view_menu_toolbar.set_active(True)
183
if self.config.get_user_option('viz-toolbar-visible') == 'False':
184
view_menu_toolbar.set_active(False)
186
162
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
188
164
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
189
165
view_menu_compact.set_active(self.compact_view)
190
166
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
192
view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
193
view_menu_diffs.set_active(False)
194
if self.config.get_user_option('viz-show-diffs') == 'True':
195
view_menu_diffs.set_active(True)
196
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
198
view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
199
view_menu_wide_diffs.set_active(False)
200
if self.config.get_user_option('viz-wide-diffs') == 'True':
201
view_menu_wide_diffs.set_active(True)
202
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
204
view_menu_wrap_diffs = gtk.CheckMenuItem("Wrap _Long Lines in Diffs")
205
view_menu_wrap_diffs.set_active(False)
206
if self.config.get_user_option('viz-wrap-diffs') == 'True':
207
view_menu_wrap_diffs.set_active(True)
208
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
210
168
view_menu.add(view_menu_toolbar)
211
169
view_menu.add(view_menu_compact)
212
170
view_menu.add(gtk.SeparatorMenuItem())
213
view_menu.add(view_menu_diffs)
214
view_menu.add(view_menu_wide_diffs)
215
view_menu.add(view_menu_wrap_diffs)
216
view_menu.add(gtk.SeparatorMenuItem())
218
172
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
219
173
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
233
187
go_menu.set_accel_group(self.accel_group)
234
188
go_menuitem = gtk.MenuItem("_Go")
235
189
go_menuitem.set_submenu(go_menu)
237
191
go_menu_next = self.next_rev_action.create_menu_item()
238
192
go_menu_prev = self.prev_rev_action.create_menu_item()
241
195
tag_image.set_from_file(icon_path("tag-16.png"))
242
196
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
243
197
self.go_menu_tags.set_image(tag_image)
244
self.treeview.connect('refreshed', lambda w: self._update_tags())
246
200
go_menu.add(go_menu_next)
247
201
go_menu.add(go_menu_prev)
248
202
go_menu.add(gtk.SeparatorMenuItem())
249
203
go_menu.add(self.go_menu_tags)
251
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
205
revision_menu = gtk.Menu()
252
206
revision_menuitem = gtk.MenuItem("_Revision")
253
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)
255
224
branch_menu = gtk.Menu()
256
225
branch_menuitem = gtk.MenuItem("_Branch")
259
228
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
260
229
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
263
from bzrlib.plugins import search
265
mutter("Didn't find search plugin")
267
branch_menu.add(gtk.SeparatorMenuItem())
269
branch_index_menuitem = gtk.MenuItem("_Index")
270
branch_index_menuitem.connect('activate', self._branch_index_cb)
271
branch_menu.add(branch_index_menuitem)
273
branch_search_menuitem = gtk.MenuItem("_Search")
274
branch_search_menuitem.connect('activate', self._branch_search_cb)
275
branch_menu.add(branch_search_menuitem)
277
231
help_menu = gtk.Menu()
278
232
help_menuitem = gtk.MenuItem("_Help")
279
233
help_menuitem.set_submenu(help_menu)
301
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())
303
264
for col in ["revno", "date"]:
304
265
option = self.config.get_user_option(col + '-column-visible')
305
266
if option is not None:
312
273
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
313
274
align.set_padding(5, 0, 0, 0)
314
275
align.add(self.treeview)
315
# user-configured size
316
size = self._load_size('viz-graph-size')
319
align.set_size_request(width, height)
321
(width, height) = self.get_size()
322
align.set_size_request(width, int(height / 2.5))
323
self._save_size_on_destroy(align, 'viz-graph-size')
349
301
def construct_bottom(self):
350
302
"""Construct the bottom half of the window."""
351
if self.config.get_user_option('viz-wide-diffs') == 'True':
352
self.diff_paned = gtk.VPaned()
354
self.diff_paned = gtk.HPaned()
355
(width, height) = self.get_size()
356
self.diff_paned.set_size_request(20, 20) # shrinkable
358
303
from bzrlib.plugins.gtk.revisionview import RevisionView
359
304
self.revisionview = RevisionView(branch=self.branch)
360
self.revisionview.set_size_request(width/3, int(height / 2.5))
361
# user-configured size
362
size = self._load_size('viz-revisionview-size')
365
self.revisionview.set_size_request(width, height)
366
self._save_size_on_destroy(self.revisionview, 'viz-revisionview-size')
305
(width, height) = self.get_size()
306
self.revisionview.set_size_request(width, int(height / 2.5))
367
307
self.revisionview.show()
368
308
self.revisionview.set_show_callback(self._show_clicked_cb)
369
309
self.revisionview.connect('notify::revision', self._go_clicked_cb)
370
310
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
371
self.diff_paned.pack1(self.revisionview)
373
from bzrlib.plugins.gtk.diff import DiffWidget
374
self.diff = DiffWidget()
375
self.diff_paned.pack2(self.diff)
377
self.diff_paned.show_all()
378
if self.config.get_user_option('viz-show-diffs') != 'True':
381
return self.diff_paned
311
return self.revisionview
383
313
def _tag_selected_cb(self, menuitem, revid):
384
314
self.treeview.set_revision_id(revid)
411
339
self.prev_rev_action.set_sensitive(False)
414
if getattr(self.prev_button, 'set_menu', None) is not None:
415
self.prev_button.set_menu(prev_menu)
342
self.prev_button.set_menu(prev_menu)
417
344
next_menu = gtk.Menu()
418
345
if len(children) > 0:
432
359
self.next_rev_action.set_sensitive(False)
435
if getattr(self.next_button, 'set_menu', None) is not None:
436
self.next_button.set_menu(next_menu)
362
self.next_button.set_menu(next_menu)
438
364
self.revisionview.set_revision(revision)
439
365
self.revisionview.set_children(children)
440
self.update_diff_panel(revision, parents)
442
367
def _tree_revision_activated(self, widget, path, col):
443
368
# TODO: more than one parent
444
369
"""Callback for when a treeview row gets activated."""
446
371
parents = self.treeview.get_parents()
448
373
if len(parents) == 0:
449
parent_id = NULL_REVISION
451
376
parent_id = parents[0]
453
378
self.show_diff(revision.revision_id, parent_id)
454
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)
456
394
def _back_clicked_cb(self, *args):
457
395
"""Callback for when the back button is clicked."""
458
396
self.treeview.back()
460
398
def _fwd_clicked_cb(self, *args):
461
399
"""Callback for when the forward button is clicked."""
462
400
self.treeview.forward()
471
409
self.show_diff(revid, parentid)
472
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)
474
428
def _set_revision_cb(self, w, revision_id):
475
429
self.treeview.set_revision_id(revision_id)
486
440
self.treeview.set_property('compact', self.compact_view)
487
441
self.treeview.refresh()
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
443
def _tag_revision_cb(self, w):
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())
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)
519
459
def _about_dialog_cb(self, w):
520
460
from bzrlib.plugins.gtk.about import AboutDialog
521
462
AboutDialog().run()
523
464
def _col_visibility_changed(self, col, property):
527
468
def _toolbar_visibility_changed(self, col):
528
469
if col.get_active():
531
472
self.toolbar.hide()
532
self.config.set_user_option('viz-toolbar-visible', col.get_active())
534
def _make_diff_nonzero_size(self):
535
"""make sure the diff isn't zero-width or zero-height"""
536
alloc = self.diff.get_allocation()
537
if (alloc.width < 10) or (alloc.height < 10):
538
width, height = self.get_size()
539
self.revisionview.set_size_request(width/3, int(height / 2.5))
541
def _diff_visibility_changed(self, col):
542
"""Hide or show the diff panel."""
545
self._make_diff_nonzero_size()
548
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
549
self.update_diff_panel()
551
def _diff_placement_changed(self, col):
552
"""Toggle the diff panel's position."""
553
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
555
old = self.paned.get_child2()
556
self.paned.remove(old)
557
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
558
self._make_diff_nonzero_size()
560
self.treeview.emit('revision-selected')
562
def _diff_wrap_changed(self, widget):
563
"""Toggle word wrap in the diff widget."""
564
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
565
self.diff._on_wraplines_toggled(widget)
474
def _show_about_cb(self, w):
475
dialog = AboutDialog()
476
dialog.connect('response', lambda d,r: d.destroy())
567
479
def _refresh_clicked(self, w):
568
480
self.treeview.refresh()
573
485
if self.branch.supports_tags():
574
486
tags = self.branch.tags.get_tag_dict().items()
575
tags.sort(reverse=True)
576
489
for tag, revid in tags:
577
490
tag_image = gtk.Image()
578
491
tag_image.set_from_file(icon_path('tag-16.png'))
579
492
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
580
493
tag_item.set_image(tag_image)
581
494
tag_item.connect('activate', self._tag_selected_cb, revid)
582
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
583
495
menu.add(tag_item)
584
496
self.go_menu_tags.set_submenu(menu)
590
502
self.go_menu_tags.show_all()
592
def _load_size(self, name):
593
"""Read and parse 'name' from self.config.
594
The value is a string, formatted as WIDTHxHEIGHT
595
Returns None, or (width, height)
597
size = self.config.get_user_option(name)
599
width, height = [int(num) for num in size.split('x')]
600
# avoid writing config every time we start
604
def show_diff(self, revid=None, parentid=NULL_REVISION):
504
def show_diff(self, revid=None, parentid=None):
605
505
"""Open a new window to show a diff between the given revisions."""
606
506
from bzrlib.plugins.gtk.diff import DiffWindow
607
507
window = DiffWindow(parent=self)
510
parentid = NULL_REVISION
609
512
rev_tree = self.branch.repository.revision_tree(revid)
610
513
parent_tree = self.branch.repository.revision_tree(parentid)
612
description = revid + " - " + self.branch._get_nick(local=True)
515
description = revid + " - " + self.branch.nick
613
516
window.set_diff(description, rev_tree, parent_tree)
616
def update_diff_panel(self, revision=None, parents=None):
617
"""Show the current revision in the diff panel."""
618
if self.config.get_user_option('viz-show-diffs') != 'True':
621
if not revision: # default to selected row
622
revision = self.treeview.get_revision()
623
if revision == NULL_REVISION:
626
if not parents: # default to selected row's parents
627
parents = self.treeview.get_parents()
628
if len(parents) == 0:
629
parent_id = NULL_REVISION
631
parent_id = parents[0]
633
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
634
parent_tree = self.branch.repository.revision_tree(parent_id)
636
self.diff.set_diff(rev_tree, parent_tree)
637
if self.config.get_user_option('viz-wrap-diffs') == 'True':
638
self.diff._on_wraplines_toggled(wrap=True)