16
from bzrlib.plugins.gtk import icon_path
17
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
16
from bzrlib.plugins.gtk.window import Window
18
17
from bzrlib.plugins.gtk.tags import AddTagDialog
19
18
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
19
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
24
20
from bzrlib.revision import Revision, NULL_REVISION
25
from bzrlib.trace import mutter
21
from bzrlib.config import BranchConfig
22
from bzrlib.config import GlobalConfig
27
24
class BranchWindow(Window):
58
53
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)
55
# Use three-quarters of the screen by default
56
screen = self.get_screen()
57
monitor = screen.get_monitor_geometry(0)
58
width = int(monitor.width * 0.75)
59
height = int(monitor.height * 0.75)
70
60
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
63
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
78
66
gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
79
67
gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
80
gtk.accel_map_add_entry("<viz>/View/Refresh", gtk.keysyms.F5, 0)
82
69
self.accel_group = gtk.AccelGroup()
83
70
self.add_accel_group(self.accel_group)
96
83
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
97
84
self.next_rev_action.connect_accelerator()
99
self.refresh_action = gtk.Action("refresh", "_Refresh", "Refresh view", gtk.STOCK_REFRESH)
100
self.refresh_action.set_accel_path("<viz>/View/Refresh")
101
self.refresh_action.set_accel_group(self.accel_group)
102
self.refresh_action.connect("activate", self._refresh_clicked)
103
self.refresh_action.connect_accelerator()
107
88
def set_revision(self, revid):
115
96
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)
97
self.paned.pack1(self.construct_top(), resize=True, shrink=False)
98
self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
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)
101
vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
102
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
125
104
vbox.pack_start(self.paned, expand=True, fill=True)
126
105
vbox.set_focus_child(self.paned)
130
109
def construct_menubar(self):
131
110
menubar = gtk.MenuBar()
148
127
edit_menuitem = gtk.MenuItem("_Edit")
149
128
edit_menuitem.set_submenu(edit_menu)
130
edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
151
132
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
152
133
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
154
135
edit_menu_globopts = gtk.MenuItem("Global Settings")
155
136
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
138
edit_menu.add(edit_menu_find)
157
139
edit_menu.add(edit_menu_branchopts)
158
140
edit_menu.add(edit_menu_globopts)
161
143
view_menuitem = gtk.MenuItem("_View")
162
144
view_menuitem.set_submenu(view_menu)
164
view_menu_refresh = self.refresh_action.create_menu_item()
165
view_menu_refresh.connect('activate', self._refresh_clicked)
167
view_menu.add(view_menu_refresh)
168
view_menu.add(gtk.SeparatorMenuItem())
170
146
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
171
147
view_menu_toolbar.set_active(True)
172
if self.config.get_user_option('viz-toolbar-visible') == 'False':
173
view_menu_toolbar.set_active(False)
175
148
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
177
150
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
178
151
view_menu_compact.set_active(self.compact_view)
179
152
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
181
view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
182
view_menu_diffs.set_active(False)
183
if self.config.get_user_option('viz-show-diffs') == 'True':
184
view_menu_diffs.set_active(True)
185
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
187
view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
188
view_menu_wide_diffs.set_active(False)
189
if self.config.get_user_option('viz-wide-diffs') == 'True':
190
view_menu_wide_diffs.set_active(True)
191
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
193
view_menu_wrap_diffs = gtk.CheckMenuItem("Wrap _Long Lines in Diffs")
194
view_menu_wrap_diffs.set_active(False)
195
if self.config.get_user_option('viz-wrap-diffs') == 'True':
196
view_menu_wrap_diffs.set_active(True)
197
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
199
154
view_menu.add(view_menu_toolbar)
200
155
view_menu.add(view_menu_compact)
201
156
view_menu.add(gtk.SeparatorMenuItem())
202
view_menu.add(view_menu_diffs)
203
view_menu.add(view_menu_wide_diffs)
204
view_menu.add(view_menu_wrap_diffs)
205
view_menu.add(gtk.SeparatorMenuItem())
207
158
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
208
159
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
226
177
go_menu_next = self.next_rev_action.create_menu_item()
227
178
go_menu_prev = self.prev_rev_action.create_menu_item()
229
tag_image = gtk.Image()
230
tag_image.set_from_file(icon_path("tag-16.png"))
231
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
232
self.go_menu_tags.set_image(tag_image)
180
self.go_menu_tags = gtk.MenuItem("_Tags")
233
181
self._update_tags()
235
183
go_menu.add(go_menu_next)
237
185
go_menu.add(gtk.SeparatorMenuItem())
238
186
go_menu.add(self.go_menu_tags)
240
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
188
revision_menu = gtk.Menu()
241
189
revision_menuitem = gtk.MenuItem("_Revision")
242
revision_menuitem.set_submenu(self.revision_menu)
190
revision_menuitem.set_submenu(revision_menu)
192
revision_menu_diff = gtk.MenuItem("View Changes")
193
revision_menu_diff.connect('activate',
194
lambda w: self.treeview.show_diff())
196
revision_menu_tag = gtk.MenuItem("Tag Revision")
197
revision_menu_tag.connect('activate', self._tag_revision_cb)
199
revision_menu.add(revision_menu_tag)
200
revision_menu.add(revision_menu_diff)
244
202
branch_menu = gtk.Menu()
245
203
branch_menuitem = gtk.MenuItem("_Branch")
248
206
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
249
207
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
252
from bzrlib.plugins import search
254
mutter("Didn't find search plugin")
256
branch_menu.add(gtk.SeparatorMenuItem())
258
branch_index_menuitem = gtk.MenuItem("_Index")
259
branch_index_menuitem.connect('activate', self._branch_index_cb)
260
branch_menu.add(branch_index_menuitem)
262
branch_search_menuitem = gtk.MenuItem("_Search")
263
branch_search_menuitem.connect('activate', self._branch_search_cb)
264
branch_menu.add(branch_search_menuitem)
266
209
help_menu = gtk.Menu()
267
210
help_menuitem = gtk.MenuItem("_Help")
268
211
help_menuitem.set_submenu(help_menu)
300
243
option = self.config.get_user_option(col + '-column-visible')
301
244
if option is not None:
302
245
self.treeview.set_property(col + '-column-visible', option == 'True')
304
self.treeview.set_property(col + '-column-visible', False)
247
self.treeview.set_property(col + '-column-visible', False)
306
248
self.treeview.show()
308
250
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
309
251
align.set_padding(5, 0, 0, 0)
310
252
align.add(self.treeview)
311
# user-configured size
312
size = self._load_size('viz-graph-size')
315
align.set_size_request(width, height)
317
(width, height) = self.get_size()
318
align.set_size_request(width, int(height / 2.5))
319
align.connect('size-allocate', self._on_size_allocate, 'viz-graph-size')
345
278
def construct_bottom(self):
346
279
"""Construct the bottom half of the window."""
347
if self.config.get_user_option('viz-wide-diffs') == 'True':
348
self.diff_paned = gtk.VPaned()
350
self.diff_paned = gtk.HPaned()
351
(width, height) = self.get_size()
352
self.diff_paned.set_size_request(20, 20) # shrinkable
354
280
from bzrlib.plugins.gtk.revisionview import RevisionView
355
281
self.revisionview = RevisionView(branch=self.branch)
356
self.revisionview.set_size_request(width/3, int(height / 2.5))
357
# user-configured size
358
size = self._load_size('viz-revisionview-size')
361
self.revisionview.set_size_request(width, height)
362
self.revisionview.connect('size-allocate', self._on_size_allocate, 'viz-revisionview-size')
282
(width, height) = self.get_size()
283
self.revisionview.set_size_request(width, int(height / 2.5))
363
284
self.revisionview.show()
364
285
self.revisionview.set_show_callback(self._show_clicked_cb)
365
286
self.revisionview.connect('notify::revision', self._go_clicked_cb)
366
287
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
367
self.diff_paned.pack1(self.revisionview)
369
from bzrlib.plugins.gtk.diff import DiffWidget
370
self.diff = DiffWidget()
371
self.diff_paned.pack2(self.diff)
373
self.diff_paned.show_all()
374
if self.config.get_user_option('viz-show-diffs') != 'True':
377
return self.diff_paned
288
return self.revisionview
379
290
def _tag_selected_cb(self, menuitem, revid):
380
291
self.treeview.set_revision_id(revid)
385
296
parents = self.treeview.get_parents()
386
297
children = self.treeview.get_children()
388
self.revision_menu.set_revision_ids([revision.revision_id])
390
if revision and revision != NULL_REVISION:
299
if revision is not None:
391
300
prev_menu = gtk.Menu()
392
301
if len(parents) > 0:
393
302
self.prev_rev_action.set_sensitive(True)
394
303
for parent_id in parents:
395
if parent_id and parent_id != NULL_REVISION:
396
parent = self.branch.repository.get_revision(parent_id)
398
str = ' (' + parent.properties['branch-nick'] + ')'
304
parent = self.branch.repository.get_revision(parent_id)
306
str = ' (' + parent.properties['branch-nick'] + ')'
402
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
403
item.connect('activate', self._set_revision_cb, parent_id)
310
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
311
item.connect('activate', self._set_revision_cb, parent_id)
405
313
prev_menu.show_all()
407
315
self.prev_rev_action.set_sensitive(False)
432
340
self.revisionview.set_revision(revision)
433
341
self.revisionview.set_children(children)
434
self.update_diff_panel(revision, parents)
436
343
def _tree_revision_activated(self, widget, path, col):
437
344
# TODO: more than one parent
438
345
"""Callback for when a treeview row gets activated."""
480
388
self.treeview.set_property('compact', self.compact_view)
481
389
self.treeview.refresh()
483
def _branch_index_cb(self, w):
484
from bzrlib.plugins.search import index as _mod_index
485
_mod_index.index_url(self.branch.base)
487
def _branch_search_cb(self, w):
488
from bzrlib.plugins.search import index as _mod_index
489
from bzrlib.plugins.gtk.search import SearchDialog
490
from bzrlib.plugins.search import errors as search_errors
391
def _tag_revision_cb(self, w):
493
index = _mod_index.open_index_url(self.branch.base)
494
except search_errors.NoSearchIndex:
495
dialog = gtk.MessageDialog(self, type=gtk.MESSAGE_QUESTION,
496
buttons=gtk.BUTTONS_OK_CANCEL,
497
message_format="This branch has not been indexed yet. "
499
if dialog.run() == gtk.RESPONSE_OK:
501
index = _mod_index.index_url(self.branch.base)
506
dialog = SearchDialog(index)
508
if dialog.run() == gtk.RESPONSE_OK:
509
self.set_revision(dialog.get_revision())
393
self.treeview.set_sensitive(False)
394
dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
395
response = dialog.run()
396
if response != gtk.RESPONSE_NONE:
399
if response == gtk.RESPONSE_OK:
400
self.treeview.add_tag(dialog.tagname, dialog._revid)
405
self.treeview.set_sensitive(True)
513
407
def _about_dialog_cb(self, w):
514
408
from bzrlib.plugins.gtk.about import AboutDialog
522
416
def _toolbar_visibility_changed(self, col):
523
417
if col.get_active():
526
420
self.toolbar.hide()
527
self.config.set_user_option('viz-toolbar-visible', col.get_active())
529
def _make_diff_nonzero_size(self):
530
"""make sure the diff isn't zero-width or zero-height"""
531
alloc = self.diff.get_allocation()
532
if (alloc.width < 10) or (alloc.height < 10):
533
width, height = self.get_size()
534
self.revisionview.set_size_request(width/3, int(height / 2.5))
536
def _diff_visibility_changed(self, col):
537
"""Hide or show the diff panel."""
540
self._make_diff_nonzero_size()
543
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
544
self.update_diff_panel()
546
def _diff_placement_changed(self, col):
547
"""Toggle the diff panel's position."""
548
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
550
old = self.paned.get_child2()
551
self.paned.remove(old)
552
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
553
self._make_diff_nonzero_size()
555
self.treeview.emit('revision-selected')
557
def _diff_wrap_changed(self, widget):
558
"""Toggle word wrap in the diff widget."""
559
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
560
self.diff._on_wraplines_toggled(widget)
562
422
def _show_about_cb(self, w):
563
423
dialog = AboutDialog()
564
424
dialog.connect('response', lambda d,r: d.destroy())
577
437
for tag, revid in tags:
578
tag_image = gtk.Image()
579
tag_image.set_from_file(icon_path('tag-16.png'))
580
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
581
tag_item.set_image(tag_image)
438
tag_item = gtk.MenuItem(tag, use_underline=False)
582
439
tag_item.connect('activate', self._tag_selected_cb, revid)
583
440
menu.add(tag_item)
584
441
self.go_menu_tags.set_submenu(menu)
590
447
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
601
self._sizes[name] = (width, height)
605
def _on_size_allocate(self, widget, allocation, name):
606
"""When window has been resized, save the new size."""
608
if name in self._sizes:
609
width, height = self._sizes[name]
611
size_changed = (width != allocation.width) or \
612
(height != allocation.height)
615
width, height = allocation.width, allocation.height
616
self._sizes[name] = (width, height)
617
value = '%sx%s' % (width, height)
618
self.config.set_user_option(name, value)
620
449
def show_diff(self, revid=None, parentid=None):
621
450
"""Open a new window to show a diff between the given revisions."""
622
451
from bzrlib.plugins.gtk.diff import DiffWindow
632
461
window.set_diff(description, rev_tree, parent_tree)
635
def update_diff_panel(self, revision=None, parents=None):
636
"""Show the current revision in the diff panel."""
637
if self.config.get_user_option('viz-show-diffs') != 'True':
640
if not revision: # default to selected row
641
revision = self.treeview.get_revision()
642
if (not revision) or (revision == NULL_REVISION):
645
if not parents: # default to selected row's parents
646
parents = self.treeview.get_parents()
647
if len(parents) == 0:
650
parent_id = parents[0]
652
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
653
parent_tree = self.branch.repository.revision_tree(parent_id)
655
self.diff.set_diff(rev_tree, parent_tree)
656
if self.config.get_user_option('viz-wrap-diffs') == 'True':
657
self.diff._on_wraplines_toggled(wrap=True)