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.osutils import format_date
18
from graph import distances, graph, same_branch
19
from graphcell import CellRendererGraph
22
class BranchWindow(gtk.Window):
11
from gi.repository import Gdk
12
from gi.repository import Gtk
14
from bzrlib.plugins.gtk import icon_path
15
from bzrlib.plugins.gtk.branchview import TreeView
16
from bzrlib.plugins.gtk.preferences import PreferencesWindow
17
from bzrlib.plugins.gtk.revisionmenu import RevisionMenu
18
from bzrlib.plugins.gtk.window import Window
20
from bzrlib.config import GlobalConfig
21
from bzrlib.revision import NULL_REVISION
22
from bzrlib.trace import mutter
25
class BranchWindow(Window):
25
28
This object represents and manages a single window containing information
26
29
for a particular branch.
30
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
32
def __init__(self, branch, start_revs, maxnum, parent=None):
33
"""Create a new BranchWindow.
35
:param branch: Branch object for branch to show.
36
:param start_revs: Revision ids of top revisions.
37
:param maxnum: Maximum number of revisions to display,
41
super(BranchWindow, self).__init__(parent=parent)
31
42
self.set_border_width(0)
32
self.set_title("bzrk")
34
# Use three-quarters of the screen by default
35
screen = self.get_screen()
36
monitor = screen.get_monitor_geometry(0)
37
width = int(monitor.width * 0.75)
38
height = int(monitor.height * 0.75)
45
self.start_revs = start_revs
47
self.config = GlobalConfig()
49
if self.config.get_user_option('viz-compact-view') == 'yes':
50
self.compact_view = True
52
self.compact_view = False
54
self.set_title(branch._get_nick(local=True) + " - revision history")
56
# user-configured window size
57
size = self._load_size('viz-window-size')
61
# Use three-quarters of the screen by default
62
screen = self.get_screen()
63
monitor = screen.get_monitor_geometry(0)
64
width = int(monitor.width * 0.75)
65
height = int(monitor.height * 0.75)
39
66
self.set_default_size(width, height)
67
self.set_size_request(width/3, height/3)
68
self._save_size_on_destroy(self, 'viz-window-size')
42
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
71
icon = self.render_icon_pixbuf(Gtk.STOCK_INDEX, Gtk.IconSize.BUTTON)
43
72
self.set_icon(icon)
45
self.accel_group = gtk.AccelGroup()
74
Gtk.AccelMap.add_entry("<viz>/Go/Next Revision", Gdk.KEY_Up, Gdk.ModifierType.MOD1_MASK)
75
Gtk.AccelMap.add_entry("<viz>/Go/Previous Revision", Gdk.KEY_Down, Gdk.ModifierType.MOD1_MASK)
76
Gtk.AccelMap.add_entry("<viz>/View/Refresh", Gdk.KEY_F5, 0)
78
self.accel_group = Gtk.AccelGroup()
46
79
self.add_accel_group(self.accel_group)
81
self.prev_rev_action = Gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", Gtk.STOCK_GO_DOWN)
82
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
83
self.prev_rev_action.set_accel_group(self.accel_group)
84
self.prev_rev_action.connect("activate", self._back_clicked_cb)
85
self.prev_rev_action.connect_accelerator()
87
self.next_rev_action = Gtk.Action("next-rev", "_Next Revision", "Go to the next revision", Gtk.STOCK_GO_UP)
88
self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
89
self.next_rev_action.set_accel_group(self.accel_group)
90
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
91
self.next_rev_action.connect_accelerator()
93
self.refresh_action = Gtk.Action("refresh", "_Refresh", "Refresh view", Gtk.STOCK_REFRESH)
94
self.refresh_action.set_accel_path("<viz>/View/Refresh")
95
self.refresh_action.set_accel_group(self.accel_group)
96
self.refresh_action.connect("activate", self._refresh_clicked)
97
self.refresh_action.connect_accelerator()
99
self.vbox = self.construct()
101
def _save_size_on_destroy(self, widget, config_name):
102
"""Creates a hook that saves the size of widget to config option
103
config_name when the window is destroyed/closed."""
105
allocation = widget.get_allocation()
106
width, height = allocation.width, allocation.height
107
value = '%sx%s' % (width, height)
108
self.config.set_user_option(config_name, value)
109
self.connect("destroy", save_size)
111
def set_revision(self, revid):
112
self.treeview.set_revision_id(revid)
50
114
def construct(self):
51
115
"""Construct the window contents."""
52
vbox = gtk.VBox(spacing=0)
116
vbox = Gtk.VBox(spacing=0)
55
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
119
# order is important here
120
paned = self.construct_paned()
121
nav = self.construct_navigation()
122
menubar = self.construct_menubar()
58
paned.pack1(self.construct_top(), resize=True, shrink=False)
59
paned.pack2(self.construct_bottom(), resize=False, shrink=True)
61
vbox.pack_start(paned, expand=True, fill=True)
124
vbox.pack_start(menubar, False, True, 0)
125
vbox.pack_start(nav, False, True, 0)
126
vbox.pack_start(paned, True, True, 0)
62
127
vbox.set_focus_child(paned)
134
def construct_paned(self):
135
"""Construct the main HPaned/VPaned contents."""
136
if self.config.get_user_option('viz-vertical') == 'True':
137
self.paned = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL)
139
self.paned = Gtk.Paned.new(Gtk.Orientation.VERTICAL)
141
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
142
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
147
def construct_menubar(self):
148
menubar = Gtk.MenuBar()
150
file_menu = Gtk.Menu()
151
file_menuitem = Gtk.MenuItem.new_with_mnemonic("_File")
152
file_menuitem.set_submenu(file_menu)
154
file_menu_close = Gtk.ImageMenuItem.new_from_stock(
155
Gtk.STOCK_CLOSE, self.accel_group)
156
file_menu_close.connect('activate', lambda x: self.destroy())
158
file_menu_quit = Gtk.ImageMenuItem.new_from_stock(
159
Gtk.STOCK_QUIT, self.accel_group)
160
file_menu_quit.connect('activate', lambda x: Gtk.main_quit())
162
if self._parent is not None:
163
file_menu.add(file_menu_close)
164
file_menu.add(file_menu_quit)
166
edit_menu = Gtk.Menu()
167
edit_menuitem = Gtk.MenuItem.new_with_mnemonic("_Edit")
168
edit_menuitem.set_submenu(edit_menu)
170
edit_menu_branchopts = Gtk.MenuItem(label="Branch Settings")
171
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
173
edit_menu_globopts = Gtk.MenuItem(label="Global Settings")
174
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
176
edit_menu.add(edit_menu_branchopts)
177
edit_menu.add(edit_menu_globopts)
179
view_menu = Gtk.Menu()
180
view_menuitem = Gtk.MenuItem.new_with_mnemonic("_View")
181
view_menuitem.set_submenu(view_menu)
183
view_menu_refresh = self.refresh_action.create_menu_item()
184
view_menu_refresh.connect('activate', self._refresh_clicked)
186
view_menu.add(view_menu_refresh)
187
view_menu.add(Gtk.SeparatorMenuItem())
189
view_menu_toolbar = Gtk.CheckMenuItem(label="Show Toolbar")
190
view_menu_toolbar.set_active(True)
191
if self.config.get_user_option('viz-toolbar-visible') == 'False':
192
view_menu_toolbar.set_active(False)
194
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
196
view_menu_compact = Gtk.CheckMenuItem(label="Show Compact Graph")
197
view_menu_compact.set_active(self.compact_view)
198
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
200
view_menu_vertical = Gtk.CheckMenuItem(label="Side-by-side Layout")
201
view_menu_vertical.set_active(False)
202
if self.config.get_user_option('viz-vertical') == 'True':
203
view_menu_vertical.set_active(True)
204
view_menu_vertical.connect('toggled', self._vertical_layout)
206
view_menu_diffs = Gtk.CheckMenuItem(label="Show Diffs")
207
view_menu_diffs.set_active(False)
208
if self.config.get_user_option('viz-show-diffs') == 'True':
209
view_menu_diffs.set_active(True)
210
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
212
view_menu_wide_diffs = Gtk.CheckMenuItem(label="Wide Diffs")
213
view_menu_wide_diffs.set_active(False)
214
if self.config.get_user_option('viz-wide-diffs') == 'True':
215
view_menu_wide_diffs.set_active(True)
216
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
218
view_menu_wrap_diffs = Gtk.CheckMenuItem.new_with_mnemonic(
219
"Wrap _Long Lines in Diffs")
220
view_menu_wrap_diffs.set_active(False)
221
if self.config.get_user_option('viz-wrap-diffs') == 'True':
222
view_menu_wrap_diffs.set_active(True)
223
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
225
view_menu.add(view_menu_toolbar)
226
view_menu.add(view_menu_compact)
227
view_menu.add(view_menu_vertical)
228
view_menu.add(Gtk.SeparatorMenuItem())
229
view_menu.add(view_menu_diffs)
230
view_menu.add(view_menu_wide_diffs)
231
view_menu.add(view_menu_wrap_diffs)
232
view_menu.add(Gtk.SeparatorMenuItem())
234
self.mnu_show_revno_column = Gtk.CheckMenuItem.new_with_mnemonic(
235
"Show Revision _Number Column")
236
self.mnu_show_date_column = Gtk.CheckMenuItem.new_with_mnemonic(
239
# Revision numbers are pointless if there are multiple branches
240
if len(self.start_revs) > 1:
241
self.mnu_show_revno_column.set_sensitive(False)
242
self.treeview.set_property('revno-column-visible', False)
244
for (col, name) in [(self.mnu_show_revno_column, "revno"),
245
(self.mnu_show_date_column, "date")]:
246
col.set_active(self.treeview.get_property(name + "-column-visible"))
247
col.connect('toggled', self._col_visibility_changed, name)
251
go_menu.set_accel_group(self.accel_group)
252
go_menuitem = Gtk.MenuItem.new_with_mnemonic("_Go")
253
go_menuitem.set_submenu(go_menu)
255
go_menu_next = self.next_rev_action.create_menu_item()
256
go_menu_prev = self.prev_rev_action.create_menu_item()
258
tag_image = Gtk.Image()
259
tag_image.set_from_file(icon_path("tag-16.png"))
260
self.go_menu_tags = Gtk.ImageMenuItem.new_with_mnemonic("_Tags")
261
self.go_menu_tags.set_image(tag_image)
262
self.treeview.connect('refreshed', lambda w: self._update_tags())
264
go_menu.add(go_menu_next)
265
go_menu.add(go_menu_prev)
266
go_menu.add(Gtk.SeparatorMenuItem())
267
go_menu.add(self.go_menu_tags)
269
self.revision_menu = RevisionMenu(self.branch.repository, [],
270
self.branch, parent=self)
271
revision_menuitem = Gtk.MenuItem.new_with_mnemonic("_Revision")
272
revision_menuitem.set_submenu(self.revision_menu)
274
branch_menu = Gtk.Menu()
275
branch_menuitem = Gtk.MenuItem.new_with_mnemonic("_Branch")
276
branch_menuitem.set_submenu(branch_menu)
278
branch_menu.add(Gtk.MenuItem.new_with_mnemonic("Pu_ll Revisions"))
279
branch_menu.add(Gtk.MenuItem.new_with_mnemonic("Pu_sh Revisions"))
282
from bzrlib.plugins import search
284
mutter("Didn't find search plugin")
286
branch_menu.add(Gtk.SeparatorMenuItem())
288
branch_index_menuitem = Gtk.MenuItem.new_with_mnemonic("_Index")
289
branch_index_menuitem.connect('activate', self._branch_index_cb)
290
branch_menu.add(branch_index_menuitem)
292
branch_search_menuitem = Gtk.MenuItem.new_with_mnemonic("_Search")
293
branch_search_menuitem.connect('activate', self._branch_search_cb)
294
branch_menu.add(branch_search_menuitem)
296
help_menu = Gtk.Menu()
297
help_menuitem = Gtk.MenuItem.new_with_mnemonic("_Help")
298
help_menuitem.set_submenu(help_menu)
300
help_about_menuitem = Gtk.ImageMenuItem.new_from_stock(
301
Gtk.STOCK_ABOUT, self.accel_group)
302
help_about_menuitem.connect('activate', self._about_dialog_cb)
304
help_menu.add(help_about_menuitem)
306
menubar.add(file_menuitem)
307
menubar.add(edit_menuitem)
308
menubar.add(view_menuitem)
309
menubar.add(go_menuitem)
310
menubar.add(revision_menuitem)
311
menubar.add(branch_menuitem)
312
menubar.add(help_menuitem)
66
317
def construct_top(self):
67
318
"""Construct the top-half of the window."""
68
scrollwin = gtk.ScrolledWindow()
69
scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
70
scrollwin.set_shadow_type(gtk.SHADOW_IN)
73
self.treeview = gtk.TreeView()
74
self.treeview.set_rules_hint(True)
75
self.treeview.set_search_column(4)
76
self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
77
self.treeview.connect("row-activated", self._treeview_row_activated_cb)
78
scrollwin.add(self.treeview)
319
# FIXME: Make broken_line_length configurable
321
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum,
324
for col in ["revno", "date"]:
325
option = self.config.get_user_option(col + '-column-visible')
326
if option is not None:
327
self.treeview.set_property(col + '-column-visible',
330
self.treeview.set_property(col + '-column-visible', False)
79
332
self.treeview.show()
81
cell = CellRendererGraph()
82
column = gtk.TreeViewColumn()
83
column.set_resizable(True)
84
column.pack_start(cell, expand=False)
85
column.add_attribute(cell, "node", 1)
86
column.add_attribute(cell, "in-lines", 2)
87
column.add_attribute(cell, "out-lines", 3)
88
self.treeview.append_column(column)
90
cell = gtk.CellRendererText()
91
cell.set_property("width-chars", 40)
92
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
93
column = gtk.TreeViewColumn("Message")
94
column.set_resizable(True)
95
column.pack_start(cell, expand=True)
96
column.add_attribute(cell, "text", 4)
97
self.treeview.append_column(column)
99
cell = gtk.CellRendererText()
100
cell.set_property("width-chars", 40)
101
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
102
column = gtk.TreeViewColumn("Committer")
103
column.set_resizable(True)
104
column.pack_start(cell, expand=True)
105
column.add_attribute(cell, "text", 5)
106
self.treeview.append_column(column)
108
cell = gtk.CellRendererText()
109
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
110
column = gtk.TreeViewColumn("Date")
111
column.set_resizable(True)
112
column.pack_start(cell, expand=True)
113
column.add_attribute(cell, "text", 6)
114
self.treeview.append_column(column)
334
align = Gtk.Alignment.new(0.0, 0.0, 1.0, 1.0)
335
align.set_padding(5, 0, 0, 0)
336
align.add(self.treeview)
337
# user-configured size
338
size = self._load_size('viz-graph-size')
341
align.set_size_request(width, height)
343
(width, height) = self.get_size()
344
align.set_size_request(width, int(height / 2.5))
345
self._save_size_on_destroy(align, 'viz-graph-size')
118
350
def construct_navigation(self):
119
351
"""Construct the navigation buttons."""
121
frame.set_shadow_type(gtk.SHADOW_OUT)
124
hbox = gtk.HBox(spacing=12)
128
self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
129
self.back_button.set_relief(gtk.RELIEF_NONE)
130
self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
132
self.back_button.connect("clicked", self._back_clicked_cb)
133
hbox.pack_start(self.back_button, expand=False, fill=True)
134
self.back_button.show()
136
self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
137
self.fwd_button.set_relief(gtk.RELIEF_NONE)
138
self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
140
self.fwd_button.connect("clicked", self._fwd_clicked_cb)
141
hbox.pack_start(self.fwd_button, expand=False, fill=True)
142
self.fwd_button.show()
352
self.toolbar = Gtk.Toolbar()
353
self.toolbar.set_style(Gtk.ToolbarStyle.BOTH_HORIZ)
355
self.prev_button = self.prev_rev_action.create_tool_item()
356
self.toolbar.insert(self.prev_button, -1)
358
self.next_button = self.next_rev_action.create_tool_item()
359
self.toolbar.insert(self.next_button, -1)
361
self.toolbar.insert(Gtk.SeparatorToolItem(), -1)
363
refresh_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_REFRESH)
364
refresh_button.connect('clicked', self._refresh_clicked)
365
self.toolbar.insert(refresh_button, -1)
367
self.toolbar.show_all()
146
371
def construct_bottom(self):
147
372
"""Construct the bottom half of the window."""
148
from bzrlib.plugins.gtk.logview import LogView
149
self.logview = LogView()
373
if self.config.get_user_option('viz-wide-diffs') == 'True':
374
self.diff_paned = Gtk.Paned.new(Gtk.Orientation.VERTICAL)
376
self.diff_paned = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL)
150
377
(width, height) = self.get_size()
151
self.logview.set_size_request(width, int(height / 2.5))
153
self.logview.set_show_callback(self._show_clicked_cb)
154
self.logview.set_go_callback(self._go_clicked_cb)
157
def set_branch(self, branch, start, maxnum):
158
"""Set the branch and start position for this window.
160
Creates a new TreeModel and populates it with information about
161
the new branch before updating the window title and model of the
166
# [ revision, node, last_lines, lines, message, committer, timestamp ]
167
self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
168
gobject.TYPE_PYOBJECT,
169
gobject.TYPE_PYOBJECT,
170
gobject.TYPE_PYOBJECT,
176
(self.revisions, colours, self.children, self.parent_ids,
177
merge_sorted) = distances(branch, start)
178
for (index, (revision, node, lines)) in enumerate(graph(
179
self.revisions, colours, merge_sorted)):
180
# FIXME: at this point we should be able to show the graph order
181
# and lines with no message or commit data - and then incrementally
182
# fill the timestamp, committer etc data as desired.
183
message = revision.message.split("\n")[0]
184
if revision.committer is not None:
185
timestamp = format_date(revision.timestamp, revision.timezone)
188
self.model.append([revision, node, last_lines, lines,
189
message, revision.committer, timestamp])
190
self.index[revision] = index
192
if maxnum is not None and index > maxnum:
195
self.set_title(branch.nick + " - bzrk")
196
self.treeview.set_model(self.model)
198
def _treeview_cursor_cb(self, *args):
199
"""Callback for when the treeview cursor changes."""
200
(path, col) = self.treeview.get_cursor()
201
revision = self.model[path][0]
203
self.back_button.set_sensitive(len(self.parent_ids[revision]) > 0)
204
self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
205
self.logview.set_revision(revision)
378
self.diff_paned.set_size_request(20, 20) # shrinkable
380
from bzrlib.plugins.gtk.revisionview import RevisionView
381
self.revisionview = RevisionView(branch=self.branch)
382
self.revisionview.set_size_request(width/3, int(height / 2.5))
383
# user-configured size
384
size = self._load_size('viz-revisionview-size')
387
self.revisionview.set_size_request(width, height)
388
self._save_size_on_destroy(self.revisionview, 'viz-revisionview-size')
389
self.revisionview.show()
390
self.revisionview.set_show_callback(self._show_clicked_cb)
391
self.revisionview.connect('notify::revision', self._go_clicked_cb)
392
self.treeview.connect('tag-added',
393
lambda w, t, r: self.revisionview.update_tags())
394
self.treeview.connect('revision-selected',
395
self._treeselection_changed_cb)
396
self.treeview.connect('revision-activated',
397
self._tree_revision_activated)
398
self.diff_paned.pack1(self.revisionview)
400
from bzrlib.plugins.gtk.diff import DiffWidget
401
self.diff = DiffWidget()
402
self.diff_paned.pack2(self.diff)
404
self.diff_paned.show_all()
405
if self.config.get_user_option('viz-show-diffs') != 'True':
408
return self.diff_paned
410
def _tag_selected_cb(self, menuitem, revid):
411
self.treeview.set_revision_id(revid)
413
def _treeselection_changed_cb(self, selection, *args):
414
"""callback for when the treeview changes."""
415
revision = self.treeview.get_revision()
416
parents = self.treeview.get_parents()
417
children = self.treeview.get_children()
419
if revision and revision.revision_id != NULL_REVISION:
420
self.revision_menu.set_revision_ids([revision.revision_id])
421
prev_menu = Gtk.Menu()
423
self.prev_rev_action.set_sensitive(True)
424
for parent_id in parents:
425
if parent_id and parent_id != NULL_REVISION:
426
parent = self.branch.repository.get_revision(parent_id)
428
str = ' (%s)' % parent.properties['branch-nick']
433
label=parent.message.split("\n")[0] + str)
434
item.connect('activate', self._set_revision_cb, parent_id)
438
self.prev_rev_action.set_sensitive(False)
441
if getattr(self.prev_button, 'set_menu', None) is not None:
442
self.prev_button.set_menu(prev_menu)
444
next_menu = Gtk.Menu()
445
if len(children) > 0:
446
self.next_rev_action.set_sensitive(True)
447
for child_id in children:
448
child = self.branch.repository.get_revision(child_id)
450
str = ' (%s)' % child.properties['branch-nick']
455
label=child.message.split("\n")[0] + str)
456
item.connect('activate', self._set_revision_cb, child_id)
460
self.next_rev_action.set_sensitive(False)
463
if getattr(self.next_button, 'set_menu', None) is not None:
464
self.next_button.set_menu(next_menu)
466
self.revisionview.set_revision(revision)
467
self.revisionview.set_children(children)
468
self.update_diff_panel(revision, parents)
470
def _tree_revision_activated(self, widget, path, col):
471
# TODO: more than one parent
472
"""Callback for when a treeview row gets activated."""
473
revision = self.treeview.get_revision()
474
parents = self.treeview.get_parents()
476
if len(parents) == 0:
477
parent_id = NULL_REVISION
479
parent_id = parents[0]
481
if revision is not None:
482
self.show_diff(revision.revision_id, parent_id)
484
self.show_diff(NULL_REVISION)
485
self.treeview.grab_focus()
207
487
def _back_clicked_cb(self, *args):
208
488
"""Callback for when the back button is clicked."""
209
(path, col) = self.treeview.get_cursor()
210
revision = self.model[path][0]
211
if not len(self.parent_ids[revision]):
214
for parent_id in self.parent_ids[revision]:
215
parent = self.revisions[parent_id]
216
if same_branch(revision, parent):
217
self.treeview.set_cursor(self.index[parent])
220
next = self.revisions[self.parent_ids[revision][0]]
221
self.treeview.set_cursor(self.index[next])
222
self.treeview.grab_focus()
224
491
def _fwd_clicked_cb(self, *args):
225
492
"""Callback for when the forward button is clicked."""
226
(path, col) = self.treeview.get_cursor()
227
revision = self.model[path][0]
228
if not len(self.children[revision]):
231
for child in self.children[revision]:
232
if same_branch(child, revision):
233
self.treeview.set_cursor(self.index[child])
236
prev = list(self.children[revision])[0]
237
self.treeview.set_cursor(self.index[prev])
238
self.treeview.grab_focus()
240
def _go_clicked_cb(self, revid):
493
self.treeview.forward()
495
def _go_clicked_cb(self, w, p):
241
496
"""Callback for when the go button for a parent is clicked."""
242
self.treeview.set_cursor(self.index[self.revisions[revid]])
497
if self.revisionview.get_revision() is not None:
498
self.treeview.set_revision(self.revisionview.get_revision())
500
def _show_clicked_cb(self, revid, parentid):
501
"""Callback for when the show button for a parent is clicked."""
502
self.show_diff(revid, parentid)
243
503
self.treeview.grab_focus()
245
def show_diff(self, branch, revid, parentid):
505
def _set_revision_cb(self, w, revision_id):
506
self.treeview.set_revision_id(revision_id)
508
def _brokenlines_toggled_cb(self, button):
509
self.compact_view = button.get_active()
511
if self.compact_view:
516
self.config.set_user_option('viz-compact-view', option)
517
self.treeview.set_property('compact', self.compact_view)
518
self.treeview.refresh()
520
def _branch_index_cb(self, w):
521
from bzrlib.plugins.search import index as _mod_index
522
_mod_index.index_url(self.branch.base)
524
def _branch_search_cb(self, w):
525
from bzrlib.plugins.search import (
527
errors as search_errors,
529
from bzrlib.plugins.gtk.search import SearchDialog
532
index = _mod_index.open_index_url(self.branch.base)
533
except search_errors.NoSearchIndex:
534
dialog = Gtk.MessageDialog(self, type=Gtk.MessageType.QUESTION,
535
buttons=Gtk.ButtonsType.OK_CANCEL,
536
message_format="This branch has not been indexed yet. "
538
if dialog.run() == Gtk.ResponseType.OK:
540
index = _mod_index.index_url(self.branch.base)
545
dialog = SearchDialog(index)
547
if dialog.run() == Gtk.ResponseType.OK:
548
revid = dialog.get_revision()
549
if revid is not None:
550
self.set_revision(revid)
554
def _about_dialog_cb(self, w):
555
from bzrlib.plugins.gtk.about import AboutDialog
558
def _col_visibility_changed(self, col, property):
559
self.config.set_user_option(property + '-column-visible', col.get_active())
560
self.treeview.set_property(property + '-column-visible', col.get_active())
562
def _toolbar_visibility_changed(self, col):
567
self.config.set_user_option('viz-toolbar-visible', col.get_active())
569
def _vertical_layout(self, col):
570
"""Toggle the layout vertical/horizontal"""
571
self.config.set_user_option('viz-vertical', str(col.get_active()))
574
self.vbox.remove(old)
575
self.vbox.pack_start(
576
self.construct_paned(), True, True, 0)
577
self._make_diff_paned_nonzero_size()
578
self._make_diff_nonzero_size()
580
self.treeview.emit('revision-selected')
582
def _make_diff_paned_nonzero_size(self):
583
"""make sure the diff/revision pane isn't zero-width or zero-height"""
584
alloc = self.diff_paned.get_allocation()
585
if (alloc.width < 10) or (alloc.height < 10):
586
width, height = self.get_size()
587
self.diff_paned.set_size_request(width/3, int(height / 2.5))
589
def _make_diff_nonzero_size(self):
590
"""make sure the diff isn't zero-width or zero-height"""
591
alloc = self.diff.get_allocation()
592
if (alloc.width < 10) or (alloc.height < 10):
593
width, height = self.get_size()
594
self.revisionview.set_size_request(width/3, int(height / 2.5))
596
def _diff_visibility_changed(self, col):
597
"""Hide or show the diff panel."""
600
self._make_diff_nonzero_size()
603
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
604
self.update_diff_panel()
606
def _diff_placement_changed(self, col):
607
"""Toggle the diff panel's position."""
608
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
610
old = self.paned.get_child2()
611
self.paned.remove(old)
612
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
613
self._make_diff_nonzero_size()
615
self.treeview.emit('revision-selected')
617
def _diff_wrap_changed(self, widget):
618
"""Toggle word wrap in the diff widget."""
619
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
620
self.diff._on_wraplines_toggled(widget)
622
def _refresh_clicked(self, w):
623
self.treeview.refresh()
625
def _update_tags(self):
628
if self.branch.supports_tags():
629
tags = self.branch.tags.get_tag_dict().items()
630
tags.sort(reverse=True)
631
for tag, revid in tags:
632
tag_image = Gtk.Image()
633
tag_image.set_from_file(icon_path('tag-16.png'))
634
tag_item = Gtk.ImageMenuItem.new_with_mnemonic(
635
tag.replace('_', '__'))
636
tag_item.set_image(tag_image)
637
tag_item.connect('activate', self._tag_selected_cb, revid)
638
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
640
self.go_menu_tags.set_submenu(menu)
642
self.go_menu_tags.set_sensitive(len(tags) != 0)
644
self.go_menu_tags.set_sensitive(False)
646
self.go_menu_tags.show_all()
648
def _load_size(self, name):
649
"""Read and parse 'name' from self.config.
650
The value is a string, formatted as WIDTHxHEIGHT
651
Returns None, or (width, height)
653
size = self.config.get_user_option(name)
655
width, height = [int(num) for num in size.split('x')]
656
# avoid writing config every time we start
660
def show_diff(self, revid, parentid=NULL_REVISION):
246
661
"""Open a new window to show a diff between the given revisions."""
247
662
from bzrlib.plugins.gtk.diff import DiffWindow
248
window = DiffWindow()
249
rev_tree = branch.repository.revision_tree(revid)
250
parent_tree = branch.repository.revision_tree(parentid)
251
description = revid + " - " + branch.nick
663
window = DiffWindow(parent=self)
665
rev_tree = self.branch.repository.revision_tree(revid)
666
parent_tree = self.branch.repository.revision_tree(parentid)
668
description = revid + " - " + self.branch._get_nick(local=True)
252
669
window.set_diff(description, rev_tree, parent_tree)
255
def _show_clicked_cb(self, revid, parentid):
256
"""Callback for when the show button for a parent is clicked."""
257
self.show_diff(self.branch, revid, parentid)
258
self.treeview.grab_focus()
260
def _treeview_row_activated_cb(self, widget, path, col):
261
# TODO: more than one parent
262
"""Callback for when a treeview row gets activated."""
263
revision = self.model[path][0]
264
if len(self.parent_ids[revision]) == 0:
265
# Ignore revisions without parent
267
parent_id = self.parent_ids[revision][0]
268
self.show_diff(self.branch, revision.revision_id, parent_id)
269
self.treeview.grab_focus()
672
def update_diff_panel(self, revision=None, parents=None):
673
"""Show the current revision in the diff panel."""
674
if self.config.get_user_option('viz-show-diffs') != 'True':
677
if not revision: # default to selected row
678
revision = self.treeview.get_revision()
679
if revision == NULL_REVISION:
682
if not parents: # default to selected row's parents
683
parents = self.treeview.get_parents()
684
if len(parents) == 0:
685
parent_id = NULL_REVISION
687
parent_id = parents[0]
689
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
690
parent_tree = self.branch.repository.revision_tree(parent_id)
692
self.diff.set_diff(rev_tree, parent_tree)
693
if self.config.get_user_option('viz-wrap-diffs') == 'True':
694
self.diff._on_wraplines_toggled(wrap=True)