1
# -*- coding: UTF-8 -*-
4
3
This module contains the code to manage the branch information window,
5
4
which contains both the revision graph and details panes.
8
__copyright__ = "Copyright © 2005 Canonical Ltd."
7
__copyright__ = "Copyright (c) 2005 Canonical Ltd."
9
8
__author__ = "Scott James Remnant <scott@ubuntu.com>"
16
from bzrlib.plugins.gtk.window import Window
17
15
from bzrlib.plugins.gtk import icon_path
16
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
18
17
from bzrlib.plugins.gtk.tags import AddTagDialog
19
18
from bzrlib.plugins.gtk.preferences import PreferencesWindow
20
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
19
from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
20
from bzrlib.plugins.gtk.window import Window
22
from bzrlib.config import BranchConfig, GlobalConfig
21
23
from bzrlib.revision import Revision, NULL_REVISION
22
from bzrlib.config import BranchConfig
23
from bzrlib.config import GlobalConfig
24
from bzrlib.trace import mutter
25
26
class BranchWindow(Window):
52
53
self.compact_view = False
54
self.set_title(branch.nick + " - revision history")
55
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)
57
# user-configured window size
58
size = self._load_size('viz-window-size')
62
# Use three-quarters of the screen by default
63
screen = self.get_screen()
64
monitor = screen.get_monitor_geometry(0)
65
width = int(monitor.width * 0.75)
66
height = int(monitor.height * 0.75)
61
67
self.set_default_size(width, height)
68
self.set_size_request(width/3, height/3)
69
self._save_size_on_destroy(self, 'viz-window-size')
64
72
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
71
79
self.accel_group = gtk.AccelGroup()
72
80
self.add_accel_group(self.accel_group)
74
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
82
if getattr(gtk.Action, 'set_tool_item_type', None) is not None:
83
# Not available before PyGtk-2.10
84
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
76
86
self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
77
87
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
106
def _save_size_on_destroy(self, widget, config_name):
107
"""Creates a hook that saves the size of widget to config option
108
config_name when the window is destroyed/closed."""
110
width, height = widget.allocation.width, widget.allocation.height
111
value = '%sx%s' % (width, height)
112
self.config.set_user_option(config_name, value)
113
self.connect("destroy", save_size)
96
115
def set_revision(self, revid):
97
116
self.treeview.set_revision_id(revid)
104
123
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)
124
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
125
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
107
126
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)
128
nav = self.construct_navigation()
129
menubar = self.construct_menubar()
130
vbox.pack_start(menubar, expand=False, fill=True)
131
vbox.pack_start(nav, expand=False, fill=True)
112
133
vbox.pack_start(self.paned, expand=True, fill=True)
113
134
vbox.set_focus_child(self.paned)
136
self.treeview.connect('revision-selected',
137
self._treeselection_changed_cb)
138
self.treeview.connect('revision-activated',
139
self._tree_revision_activated)
141
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
117
144
def construct_menubar(self):
118
145
menubar = gtk.MenuBar()
135
162
edit_menuitem = gtk.MenuItem("_Edit")
136
163
edit_menuitem.set_submenu(edit_menu)
138
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
140
165
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
141
166
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
143
168
edit_menu_globopts = gtk.MenuItem("Global Settings")
144
169
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
146
edit_menu.add(edit_menu_find)
147
171
edit_menu.add(edit_menu_branchopts)
148
172
edit_menu.add(edit_menu_globopts)
160
184
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
161
185
view_menu_toolbar.set_active(True)
186
if self.config.get_user_option('viz-toolbar-visible') == 'False':
187
view_menu_toolbar.set_active(False)
162
189
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
164
191
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
165
192
view_menu_compact.set_active(self.compact_view)
166
193
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
195
view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
196
view_menu_diffs.set_active(False)
197
if self.config.get_user_option('viz-show-diffs') == 'True':
198
view_menu_diffs.set_active(True)
199
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
201
view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
202
view_menu_wide_diffs.set_active(False)
203
if self.config.get_user_option('viz-wide-diffs') == 'True':
204
view_menu_wide_diffs.set_active(True)
205
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
207
view_menu_wrap_diffs = gtk.CheckMenuItem("Wrap _Long Lines in Diffs")
208
view_menu_wrap_diffs.set_active(False)
209
if self.config.get_user_option('viz-wrap-diffs') == 'True':
210
view_menu_wrap_diffs.set_active(True)
211
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
168
213
view_menu.add(view_menu_toolbar)
169
214
view_menu.add(view_menu_compact)
170
215
view_menu.add(gtk.SeparatorMenuItem())
216
view_menu.add(view_menu_diffs)
217
view_menu.add(view_menu_wide_diffs)
218
view_menu.add(view_menu_wrap_diffs)
219
view_menu.add(gtk.SeparatorMenuItem())
172
221
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
173
222
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
195
244
tag_image.set_from_file(icon_path("tag-16.png"))
196
245
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
197
246
self.go_menu_tags.set_image(tag_image)
247
self.treeview.connect('refreshed', lambda w: self._update_tags())
200
249
go_menu.add(go_menu_next)
201
250
go_menu.add(go_menu_prev)
202
251
go_menu.add(gtk.SeparatorMenuItem())
203
252
go_menu.add(self.go_menu_tags)
205
revision_menu = gtk.Menu()
254
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
206
255
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)
256
revision_menuitem.set_submenu(self.revision_menu)
224
258
branch_menu = gtk.Menu()
225
259
branch_menuitem = gtk.MenuItem("_Branch")
228
262
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
229
263
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
266
from bzrlib.plugins import search
268
mutter("Didn't find search plugin")
270
branch_menu.add(gtk.SeparatorMenuItem())
272
branch_index_menuitem = gtk.MenuItem("_Index")
273
branch_index_menuitem.connect('activate', self._branch_index_cb)
274
branch_menu.add(branch_index_menuitem)
276
branch_search_menuitem = gtk.MenuItem("_Search")
277
branch_search_menuitem.connect('activate', self._branch_search_cb)
278
branch_menu.add(branch_search_menuitem)
231
280
help_menu = gtk.Menu()
232
281
help_menuitem = gtk.MenuItem("_Help")
233
282
help_menuitem.set_submenu(help_menu)
255
304
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
306
for col in ["revno", "date"]:
265
307
option = self.config.get_user_option(col + '-column-visible')
266
308
if option is not None:
273
315
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
274
316
align.set_padding(5, 0, 0, 0)
275
317
align.add(self.treeview)
318
# user-configured size
319
size = self._load_size('viz-graph-size')
322
align.set_size_request(width, height)
324
(width, height) = self.get_size()
325
align.set_size_request(width, int(height / 2.5))
326
self._save_size_on_destroy(align, 'viz-graph-size')
301
352
def construct_bottom(self):
302
353
"""Construct the bottom half of the window."""
354
if self.config.get_user_option('viz-wide-diffs') == 'True':
355
self.diff_paned = gtk.VPaned()
357
self.diff_paned = gtk.HPaned()
358
(width, height) = self.get_size()
359
self.diff_paned.set_size_request(20, 20) # shrinkable
303
361
from bzrlib.plugins.gtk.revisionview import RevisionView
304
362
self.revisionview = RevisionView(branch=self.branch)
305
(width, height) = self.get_size()
306
self.revisionview.set_size_request(width, int(height / 2.5))
363
self.revisionview.set_size_request(width/3, int(height / 2.5))
364
# user-configured size
365
size = self._load_size('viz-revisionview-size')
368
self.revisionview.set_size_request(width, height)
369
self._save_size_on_destroy(self.revisionview, 'viz-revisionview-size')
307
370
self.revisionview.show()
308
371
self.revisionview.set_show_callback(self._show_clicked_cb)
309
372
self.revisionview.connect('notify::revision', self._go_clicked_cb)
310
373
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
311
return self.revisionview
374
self.diff_paned.pack1(self.revisionview)
376
from bzrlib.plugins.gtk.diff import DiffWidget
377
self.diff = DiffWidget()
378
self.diff_paned.pack2(self.diff)
380
self.diff_paned.show_all()
381
if self.config.get_user_option('viz-show-diffs') != 'True':
384
return self.diff_paned
313
386
def _tag_selected_cb(self, menuitem, revid):
314
387
self.treeview.set_revision_id(revid)
364
439
self.revisionview.set_revision(revision)
365
440
self.revisionview.set_children(children)
441
self.update_diff_panel(revision, parents)
367
443
def _tree_revision_activated(self, widget, path, col):
368
444
# TODO: more than one parent
369
445
"""Callback for when a treeview row gets activated."""
371
447
parents = self.treeview.get_parents()
373
449
if len(parents) == 0:
450
parent_id = NULL_REVISION
376
452
parent_id = parents[0]
378
454
self.show_diff(revision.revision_id, parent_id)
379
455
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
457
def _back_clicked_cb(self, *args):
395
458
"""Callback for when the back button is clicked."""
396
459
self.treeview.back()
409
472
self.show_diff(revid, parentid)
410
473
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
475
def _set_revision_cb(self, w, revision_id):
429
476
self.treeview.set_revision_id(revision_id)
440
487
self.treeview.set_property('compact', self.compact_view)
441
488
self.treeview.refresh()
443
def _tag_revision_cb(self, w):
490
def _branch_index_cb(self, w):
491
from bzrlib.plugins.search import index as _mod_index
492
_mod_index.index_url(self.branch.base)
494
def _branch_search_cb(self, w):
495
from bzrlib.plugins.search import index as _mod_index
496
from bzrlib.plugins.gtk.search import SearchDialog
497
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)
500
index = _mod_index.open_index_url(self.branch.base)
501
except search_errors.NoSearchIndex:
502
dialog = gtk.MessageDialog(self, type=gtk.MESSAGE_QUESTION,
503
buttons=gtk.BUTTONS_OK_CANCEL,
504
message_format="This branch has not been indexed yet. "
506
if dialog.run() == gtk.RESPONSE_OK:
508
index = _mod_index.index_url(self.branch.base)
513
dialog = SearchDialog(index)
515
if dialog.run() == gtk.RESPONSE_OK:
516
self.set_revision(dialog.get_revision())
459
520
def _about_dialog_cb(self, w):
460
521
from bzrlib.plugins.gtk.about import AboutDialog
468
529
def _toolbar_visibility_changed(self, col):
469
530
if col.get_active():
472
533
self.toolbar.hide()
534
self.config.set_user_option('viz-toolbar-visible', col.get_active())
536
def _make_diff_nonzero_size(self):
537
"""make sure the diff isn't zero-width or zero-height"""
538
alloc = self.diff.get_allocation()
539
if (alloc.width < 10) or (alloc.height < 10):
540
width, height = self.get_size()
541
self.revisionview.set_size_request(width/3, int(height / 2.5))
543
def _diff_visibility_changed(self, col):
544
"""Hide or show the diff panel."""
547
self._make_diff_nonzero_size()
550
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
551
self.update_diff_panel()
553
def _diff_placement_changed(self, col):
554
"""Toggle the diff panel's position."""
555
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
557
old = self.paned.get_child2()
558
self.paned.remove(old)
559
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
560
self._make_diff_nonzero_size()
562
self.treeview.emit('revision-selected')
564
def _diff_wrap_changed(self, widget):
565
"""Toggle word wrap in the diff widget."""
566
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
567
self.diff._on_wraplines_toggled(widget)
474
569
def _show_about_cb(self, w):
475
570
dialog = AboutDialog()
476
571
dialog.connect('response', lambda d,r: d.destroy())
485
580
if self.branch.supports_tags():
486
581
tags = self.branch.tags.get_tag_dict().items()
582
tags.sort(reverse=True)
489
583
for tag, revid in tags:
490
584
tag_image = gtk.Image()
491
585
tag_image.set_from_file(icon_path('tag-16.png'))
492
586
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
493
587
tag_item.set_image(tag_image)
494
588
tag_item.connect('activate', self._tag_selected_cb, revid)
589
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
495
590
menu.add(tag_item)
496
591
self.go_menu_tags.set_submenu(menu)
502
597
self.go_menu_tags.show_all()
504
def show_diff(self, revid=None, parentid=None):
599
def _load_size(self, name):
600
"""Read and parse 'name' from self.config.
601
The value is a string, formatted as WIDTHxHEIGHT
602
Returns None, or (width, height)
604
size = self.config.get_user_option(name)
606
width, height = [int(num) for num in size.split('x')]
607
# avoid writing config every time we start
611
def show_diff(self, revid=None, parentid=NULL_REVISION):
505
612
"""Open a new window to show a diff between the given revisions."""
506
613
from bzrlib.plugins.gtk.diff import DiffWindow
507
614
window = DiffWindow(parent=self)
510
parentid = NULL_REVISION
512
616
rev_tree = self.branch.repository.revision_tree(revid)
513
617
parent_tree = self.branch.repository.revision_tree(parentid)
515
description = revid + " - " + self.branch.nick
619
description = revid + " - " + self.branch._get_nick(local=True)
516
620
window.set_diff(description, rev_tree, parent_tree)
623
def update_diff_panel(self, revision=None, parents=None):
624
"""Show the current revision in the diff panel."""
625
if self.config.get_user_option('viz-show-diffs') != 'True':
628
if not revision: # default to selected row
629
revision = self.treeview.get_revision()
630
if revision == NULL_REVISION:
633
if not parents: # default to selected row's parents
634
parents = self.treeview.get_parents()
635
if len(parents) == 0:
636
parent_id = NULL_REVISION
638
parent_id = parents[0]
640
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
641
parent_tree = self.branch.repository.revision_tree(parent_id)
643
self.diff.set_diff(rev_tree, parent_tree)
644
if self.config.get_user_option('viz-wrap-diffs') == 'True':
645
self.diff._on_wraplines_toggled(wrap=True)