16
from bzrlib.plugins.gtk import icon_path
17
from bzrlib.plugins.gtk.branchview import TreeView, treemodel
18
from bzrlib.plugins.gtk.tags import AddTagDialog
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
24
from bzrlib.revision import Revision, NULL_REVISION
25
from bzrlib.trace import mutter
27
class BranchWindow(Window):
17
from bzrlib.osutils import format_date
19
from graph import distances, graph, same_branch
20
from graphcell import CellRendererGraph
23
class BranchWindow(gtk.Window):
30
26
This object represents and manages a single window containing information
31
27
for a particular branch.
34
def __init__(self, branch, start_revs, maxnum, parent=None):
35
"""Create a new BranchWindow.
37
:param branch: Branch object for branch to show.
38
:param start_revs: Revision ids of top revisions.
39
:param maxnum: Maximum number of revisions to display,
43
Window.__init__(self, parent=parent)
30
def __init__(self, app=None):
31
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
44
32
self.set_border_width(0)
47
self.start_revs = start_revs
49
self.config = GlobalConfig()
51
self._sizes = {} # window and widget sizes
53
if self.config.get_user_option('viz-compact-view') == 'yes':
54
self.compact_view = True
56
self.compact_view = False
58
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)
33
self.set_title("bzrk")
37
# Use three-quarters of the screen by default
38
screen = self.get_screen()
39
monitor = screen.get_monitor_geometry(0)
40
width = int(monitor.width * 0.75)
41
height = int(monitor.height * 0.75)
70
42
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
45
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
76
46
self.set_icon(icon)
78
gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
79
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
48
self.accel_group = gtk.AccelGroup()
83
49
self.add_accel_group(self.accel_group)
85
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
87
self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
88
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
89
self.prev_rev_action.set_accel_group(self.accel_group)
90
self.prev_rev_action.connect("activate", self._back_clicked_cb)
91
self.prev_rev_action.connect_accelerator()
93
self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
94
self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
95
self.next_rev_action.set_accel_group(self.accel_group)
96
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
97
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
def set_revision(self, revid):
108
self.treeview.set_revision_id(revid)
110
53
def construct(self):
111
54
"""Construct the window contents."""
112
vbox = gtk.VBox(spacing=0)
115
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)
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)
125
vbox.pack_start(self.paned, expand=True, fill=True)
126
vbox.set_focus_child(self.paned)
130
def construct_menubar(self):
131
menubar = gtk.MenuBar()
133
file_menu = gtk.Menu()
134
file_menuitem = gtk.MenuItem("_File")
135
file_menuitem.set_submenu(file_menu)
137
file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
138
file_menu_close.connect('activate', lambda x: self.destroy())
140
file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
141
file_menu_quit.connect('activate', lambda x: gtk.main_quit())
143
if self._parent is not None:
144
file_menu.add(file_menu_close)
145
file_menu.add(file_menu_quit)
147
edit_menu = gtk.Menu()
148
edit_menuitem = gtk.MenuItem("_Edit")
149
edit_menuitem.set_submenu(edit_menu)
151
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
152
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
154
edit_menu_globopts = gtk.MenuItem("Global Settings")
155
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
157
edit_menu.add(edit_menu_branchopts)
158
edit_menu.add(edit_menu_globopts)
160
view_menu = gtk.Menu()
161
view_menuitem = gtk.MenuItem("_View")
162
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
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
171
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
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
177
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
178
view_menu_compact.set_active(self.compact_view)
179
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
view_menu.add(view_menu_toolbar)
200
view_menu.add(view_menu_compact)
201
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
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
208
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
210
# Revision numbers are pointless if there are multiple branches
211
if len(self.start_revs) > 1:
212
self.mnu_show_revno_column.set_sensitive(False)
213
self.treeview.set_property('revno-column-visible', False)
215
for (col, name) in [(self.mnu_show_revno_column, "revno"),
216
(self.mnu_show_date_column, "date")]:
217
col.set_active(self.treeview.get_property(name + "-column-visible"))
218
col.connect('toggled', self._col_visibility_changed, name)
222
go_menu.set_accel_group(self.accel_group)
223
go_menuitem = gtk.MenuItem("_Go")
224
go_menuitem.set_submenu(go_menu)
226
go_menu_next = self.next_rev_action.create_menu_item()
227
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)
235
go_menu.add(go_menu_next)
236
go_menu.add(go_menu_prev)
237
go_menu.add(gtk.SeparatorMenuItem())
238
go_menu.add(self.go_menu_tags)
240
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
241
revision_menuitem = gtk.MenuItem("_Revision")
242
revision_menuitem.set_submenu(self.revision_menu)
244
branch_menu = gtk.Menu()
245
branch_menuitem = gtk.MenuItem("_Branch")
246
branch_menuitem.set_submenu(branch_menu)
248
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
249
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
help_menu = gtk.Menu()
267
help_menuitem = gtk.MenuItem("_Help")
268
help_menuitem.set_submenu(help_menu)
270
help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
271
help_about_menuitem.connect('activate', self._about_dialog_cb)
273
help_menu.add(help_about_menuitem)
275
menubar.add(file_menuitem)
276
menubar.add(edit_menuitem)
277
menubar.add(view_menuitem)
278
menubar.add(go_menuitem)
279
menubar.add(revision_menuitem)
280
menubar.add(branch_menuitem)
281
menubar.add(help_menuitem)
56
paned.pack1(self.construct_top(), resize=True, shrink=False)
57
paned.pack2(self.construct_bottom(), resize=False, shrink=True)
286
61
def construct_top(self):
287
62
"""Construct the top-half of the window."""
288
# FIXME: Make broken_line_length configurable
290
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
292
self.treeview.connect('revision-selected',
293
self._treeselection_changed_cb)
294
self.treeview.connect('revision-activated',
295
self._tree_revision_activated)
297
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
299
for col in ["revno", "date"]:
300
option = self.config.get_user_option(col + '-column-visible')
301
if option is not None:
302
self.treeview.set_property(col + '-column-visible', option == 'True')
304
self.treeview.set_property(col + '-column-visible', False)
63
vbox = gtk.VBox(spacing=6)
64
vbox.set_border_width(12)
67
scrollwin = gtk.ScrolledWindow()
68
scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
69
scrollwin.set_shadow_type(gtk.SHADOW_IN)
70
vbox.pack_start(scrollwin, expand=True, fill=True)
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
scrollwin.add(self.treeview)
306
78
self.treeview.show()
308
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
309
align.set_padding(5, 0, 0, 0)
310
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')
324
def construct_navigation(self):
325
"""Construct the navigation buttons."""
326
self.toolbar = gtk.Toolbar()
327
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
329
self.prev_button = self.prev_rev_action.create_tool_item()
330
self.toolbar.insert(self.prev_button, -1)
332
self.next_button = self.next_rev_action.create_tool_item()
333
self.toolbar.insert(self.next_button, -1)
335
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
337
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
338
refresh_button.connect('clicked', self._refresh_clicked)
339
self.toolbar.insert(refresh_button, -1)
341
self.toolbar.show_all()
80
cell = CellRendererGraph()
81
column = gtk.TreeViewColumn()
82
column.set_resizable(False)
83
column.pack_start(cell, expand=False)
84
column.add_attribute(cell, "node", 1)
85
column.add_attribute(cell, "in-lines", 2)
86
column.add_attribute(cell, "out-lines", 3)
87
self.treeview.append_column(column)
89
cell = gtk.CellRendererText()
90
cell.set_property("width-chars", 40)
91
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
92
column = gtk.TreeViewColumn("Message")
93
column.set_resizable(True)
94
column.pack_start(cell, expand=True)
95
column.add_attribute(cell, "text", 4)
96
self.treeview.append_column(column)
98
cell = gtk.CellRendererText()
99
cell.set_property("width-chars", 40)
100
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
101
column = gtk.TreeViewColumn("Committer")
102
column.set_resizable(True)
103
column.pack_start(cell, expand=True)
104
column.add_attribute(cell, "text", 5)
105
self.treeview.append_column(column)
107
cell = gtk.CellRendererText()
108
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
109
column = gtk.TreeViewColumn("Date")
110
column.set_resizable(True)
111
column.pack_start(cell, expand=True)
112
column.add_attribute(cell, "text", 6)
113
self.treeview.append_column(column)
115
hbox = gtk.HBox(False, spacing=6)
116
vbox.pack_start(hbox, expand=False, fill=False)
119
self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
120
self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
122
self.back_button.connect("clicked", self._back_clicked_cb)
123
hbox.pack_start(self.back_button, expand=False, fill=True)
124
self.back_button.show()
126
self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
127
self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
129
self.fwd_button.connect("clicked", self._fwd_clicked_cb)
130
hbox.pack_start(self.fwd_button, expand=False, fill=True)
131
self.fwd_button.show()
345
135
def construct_bottom(self):
346
136
"""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()
137
vbox = gtk.VBox(False, spacing=6)
138
vbox.set_border_width(12)
351
139
(width, height) = self.get_size()
352
self.diff_paned.set_size_request(20, 20) # shrinkable
354
from bzrlib.plugins.gtk.revisionview import RevisionView
355
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')
363
self.revisionview.show()
364
self.revisionview.set_show_callback(self._show_clicked_cb)
365
self.revisionview.connect('notify::revision', self._go_clicked_cb)
366
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
379
def _tag_selected_cb(self, menuitem, revid):
380
self.treeview.set_revision_id(revid)
382
def _treeselection_changed_cb(self, selection, *args):
383
"""callback for when the treeview changes."""
384
revision = self.treeview.get_revision()
385
parents = self.treeview.get_parents()
386
children = self.treeview.get_children()
388
self.revision_menu.set_revision_ids([revision.revision_id])
390
if revision and revision != NULL_REVISION:
391
prev_menu = gtk.Menu()
393
self.prev_rev_action.set_sensitive(True)
394
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'] + ')'
402
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
403
item.connect('activate', self._set_revision_cb, parent_id)
407
self.prev_rev_action.set_sensitive(False)
410
self.prev_button.set_menu(prev_menu)
412
next_menu = gtk.Menu()
413
if len(children) > 0:
414
self.next_rev_action.set_sensitive(True)
415
for child_id in children:
416
child = self.branch.repository.get_revision(child_id)
418
str = ' (' + child.properties['branch-nick'] + ')'
422
item = gtk.MenuItem(child.message.split("\n")[0] + str)
423
item.connect('activate', self._set_revision_cb, child_id)
427
self.next_rev_action.set_sensitive(False)
430
self.next_button.set_menu(next_menu)
432
self.revisionview.set_revision(revision)
433
self.revisionview.set_children(children)
434
self.update_diff_panel(revision, parents)
436
def _tree_revision_activated(self, widget, path, col):
437
# TODO: more than one parent
438
"""Callback for when a treeview row gets activated."""
439
revision = self.treeview.get_revision()
440
parents = self.treeview.get_parents()
442
if len(parents) == 0:
140
vbox.set_size_request(width, int(height / 2.5))
143
self.table = gtk.Table(rows=5, columns=2)
144
self.table.set_row_spacings(6)
145
self.table.set_col_spacings(6)
146
vbox.pack_start(self.table, expand=False, fill=True)
149
align = gtk.Alignment(0.0, 0.5)
151
label.set_markup("<b>Revision:</b>")
153
self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
157
align = gtk.Alignment(0.0, 0.5)
158
self.revid_label = gtk.Label()
159
self.revid_label.set_selectable(True)
160
align.add(self.revid_label)
161
self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
162
self.revid_label.show()
165
align = gtk.Alignment(0.0, 0.5)
167
label.set_markup("<b>Committer:</b>")
169
self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
173
align = gtk.Alignment(0.0, 0.5)
174
self.committer_label = gtk.Label()
175
self.committer_label.set_selectable(True)
176
align.add(self.committer_label)
177
self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
178
self.committer_label.show()
181
align = gtk.Alignment(0.0, 0.5)
183
label.set_markup("<b>Branch nick:</b>")
185
self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
189
align = gtk.Alignment(0.0, 0.5)
190
self.branchnick_label = gtk.Label()
191
self.branchnick_label.set_selectable(True)
192
align.add(self.branchnick_label)
193
self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
194
self.branchnick_label.show()
197
align = gtk.Alignment(0.0, 0.5)
199
label.set_markup("<b>Timestamp:</b>")
201
self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
205
align = gtk.Alignment(0.0, 0.5)
206
self.timestamp_label = gtk.Label()
207
self.timestamp_label.set_selectable(True)
208
align.add(self.timestamp_label)
209
self.table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
210
self.timestamp_label.show()
213
align = gtk.Alignment(0.0, 0.5)
215
label.set_markup("<b>Parents:</b>")
217
self.table.attach(align, 0, 1, 4, 5, gtk.FILL, gtk.FILL)
221
self.parents_widgets = []
223
scrollwin = gtk.ScrolledWindow()
224
scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
225
scrollwin.set_shadow_type(gtk.SHADOW_IN)
226
vbox.pack_start(scrollwin, expand=True, fill=True)
229
self.message_buffer = gtk.TextBuffer()
230
textview = gtk.TextView(self.message_buffer)
231
textview.set_editable(False)
232
textview.set_wrap_mode(gtk.WRAP_WORD)
233
textview.modify_font(pango.FontDescription("Monospace"))
234
scrollwin.add(textview)
239
def set_branch(self, branch, start, maxnum):
240
"""Set the branch and start position for this window.
242
Creates a new TreeModel and populates it with information about
243
the new branch before updating the window title and model of the
248
# [ revision, node, last_lines, lines, message, committer, timestamp ]
249
self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
250
gobject.TYPE_PYOBJECT,
251
gobject.TYPE_PYOBJECT,
252
gobject.TYPE_PYOBJECT,
258
(self.revisions, colours, self.children, self.parent_ids,
259
merge_sorted) = distances(branch, start, maxnum)
260
for revision, node, lines in graph(
261
self.revisions, colours, merge_sorted):
262
# FIXME: at this point we should be able to show the graph order and
263
# lines with no message or commit data - and then incrementally fill
264
# the timestamp, committer etc data as desired.
265
message = revision.message.split("\n")[0]
266
if revision.committer is not None:
267
timestamp = format_date(revision.timestamp, revision.timezone)
270
self.model.append([ revision, node, last_lines, lines,
271
message, revision.committer, timestamp ])
272
self.index[revision] = index
277
self.set_title(branch.nick + " - bzrk")
278
self.treeview.set_model(self.model)
280
def _treeview_cursor_cb(self, *args):
281
"""Callback for when the treeview cursor changes."""
282
(path, col) = self.treeview.get_cursor()
283
revision = self.model[path][0]
285
self.back_button.set_sensitive(len(self.parent_ids[revision]) > 0)
286
self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
288
if revision.committer is not None:
290
committer = revision.committer
291
timestamp = format_date(revision.timestamp, revision.timezone)
292
message = revision.message
294
branchnick = revision.properties['branch-nick']
445
parent_id = parents[0]
447
self.show_diff(revision.revision_id, parent_id)
448
self.treeview.grab_focus()
304
self.revid_label.set_text(revision.revision_id)
305
self.branchnick_label.set_text(branchnick)
307
self.committer_label.set_text(committer)
308
self.timestamp_label.set_text(timestamp)
309
self.message_buffer.set_text(message)
311
for widget in self.parents_widgets:
312
self.table.remove(widget)
314
self.parents_widgets = []
315
self.table.resize(5 + len(self.parent_ids[revision]) - 1, 2)
316
for idx, parent_id in enumerate(self.parent_ids[revision]):
317
self.table.set_row_spacing(idx + 3, 0)
319
align = gtk.Alignment(0.0, 0.0)
320
self.parents_widgets.append(align)
321
self.table.attach(align, 1, 2, idx + 4, idx + 5,
322
gtk.EXPAND | gtk.FILL, gtk.FILL)
325
hbox = gtk.HBox(False, 0)
329
label = gtk.Label(parent_id)
330
label.set_selectable(True)
331
hbox.pack_start(label, expand=False, fill=True)
335
image.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
338
button = gtk.Button()
340
button.set_relief(gtk.RELIEF_NONE)
341
button.connect("clicked", self._go_clicked_cb, parent_id)
342
hbox.pack_start(button, expand=False, fill=True)
346
image.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
349
button = gtk.Button()
351
button.set_relief(gtk.RELIEF_NONE)
352
button.set_sensitive(self.app is not None)
353
button.connect("clicked", self._show_clicked_cb,
354
revision.revision_id, parent_id)
355
hbox.pack_start(button, expand=False, fill=True)
450
358
def _back_clicked_cb(self, *args):
451
359
"""Callback for when the back button is clicked."""
360
(path, col) = self.treeview.get_cursor()
361
revision = self.model[path][0]
362
if not len(self.parent_ids[revision]):
365
for parent_id in self.parent_ids[revision]:
366
parent = self.revisions[parent_id]
367
if same_branch(revision, parent):
368
self.treeview.set_cursor(self.index[parent])
371
next = self.revisions[self.parent_ids[revision][0]]
372
self.treeview.set_cursor(self.index[next])
373
self.treeview.grab_focus()
454
375
def _fwd_clicked_cb(self, *args):
455
376
"""Callback for when the forward button is clicked."""
456
self.treeview.forward()
458
def _go_clicked_cb(self, w, p):
377
(path, col) = self.treeview.get_cursor()
378
revision = self.model[path][0]
379
if not len(self.children[revision]):
382
for child in self.children[revision]:
383
if same_branch(child, revision):
384
self.treeview.set_cursor(self.index[child])
387
prev = list(self.children[revision])[0]
388
self.treeview.set_cursor(self.index[prev])
389
self.treeview.grab_focus()
391
def _go_clicked_cb(self, widget, revid):
459
392
"""Callback for when the go button for a parent is clicked."""
460
if self.revisionview.get_revision() is not None:
461
self.treeview.set_revision(self.revisionview.get_revision())
393
self.treeview.set_cursor(self.index[self.revisions[revid]])
394
self.treeview.grab_focus()
463
def _show_clicked_cb(self, revid, parentid):
396
def _show_clicked_cb(self, widget, revid, parentid):
464
397
"""Callback for when the show button for a parent is clicked."""
465
self.show_diff(revid, parentid)
398
if self.app is not None:
399
self.app.show_diff(self.branch, revid, parentid)
466
400
self.treeview.grab_focus()
468
def _set_revision_cb(self, w, revision_id):
469
self.treeview.set_revision_id(revision_id)
471
def _brokenlines_toggled_cb(self, button):
472
self.compact_view = button.get_active()
474
if self.compact_view:
479
self.config.set_user_option('viz-compact-view', option)
480
self.treeview.set_property('compact', self.compact_view)
481
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
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())
513
def _about_dialog_cb(self, w):
514
from bzrlib.plugins.gtk.about import AboutDialog
518
def _col_visibility_changed(self, col, property):
519
self.config.set_user_option(property + '-column-visible', col.get_active())
520
self.treeview.set_property(property + '-column-visible', col.get_active())
522
def _toolbar_visibility_changed(self, col):
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
def _show_about_cb(self, w):
563
dialog = AboutDialog()
564
dialog.connect('response', lambda d,r: d.destroy())
567
def _refresh_clicked(self, w):
568
self.treeview.refresh()
570
def _update_tags(self):
573
if self.branch.supports_tags():
574
tags = self.branch.tags.get_tag_dict().items()
577
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)
582
tag_item.connect('activate', self._tag_selected_cb, revid)
584
self.go_menu_tags.set_submenu(menu)
586
self.go_menu_tags.set_sensitive(len(tags) != 0)
588
self.go_menu_tags.set_sensitive(False)
590
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
def show_diff(self, revid=None, parentid=None):
621
"""Open a new window to show a diff between the given revisions."""
622
from bzrlib.plugins.gtk.diff import DiffWindow
623
window = DiffWindow(parent=self)
626
parentid = NULL_REVISION
628
rev_tree = self.branch.repository.revision_tree(revid)
629
parent_tree = self.branch.repository.revision_tree(parentid)
631
description = revid + " - " + self.branch.nick
632
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)