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
if self.config.get_user_option('viz-compact-view') == 'yes':
52
self.compact_view = True
54
self.compact_view = False
56
self.set_title(branch._get_nick(local=True) + " - revision history")
58
# user-configured window size
59
size = self._load_size('viz-window-size')
63
# Use three-quarters of the screen by default
64
screen = self.get_screen()
65
monitor = screen.get_monitor_geometry(0)
66
width = int(monitor.width * 0.75)
67
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)
68
42
self.set_default_size(width, height)
69
self.set_size_request(width/3, height/3)
70
self._save_size_on_destroy(self, 'viz-window-size')
73
45
icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
74
46
self.set_icon(icon)
76
gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
77
gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
78
gtk.accel_map_add_entry("<viz>/View/Refresh", gtk.keysyms.F5, 0)
80
48
self.accel_group = gtk.AccelGroup()
81
49
self.add_accel_group(self.accel_group)
83
gtk.Action.set_tool_item_type(gtk.MenuToolButton)
85
self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
86
self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
87
self.prev_rev_action.set_accel_group(self.accel_group)
88
self.prev_rev_action.connect("activate", self._back_clicked_cb)
89
self.prev_rev_action.connect_accelerator()
91
self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
92
self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
93
self.next_rev_action.set_accel_group(self.accel_group)
94
self.next_rev_action.connect("activate", self._fwd_clicked_cb)
95
self.next_rev_action.connect_accelerator()
97
self.refresh_action = gtk.Action("refresh", "_Refresh", "Refresh view", gtk.STOCK_REFRESH)
98
self.refresh_action.set_accel_path("<viz>/View/Refresh")
99
self.refresh_action.set_accel_group(self.accel_group)
100
self.refresh_action.connect("activate", self._refresh_clicked)
101
self.refresh_action.connect_accelerator()
105
def _save_size_on_destroy(self, widget, config_name):
106
"""Creates a hook that saves the size of widget to config option
107
config_name when the window is destroyed/closed."""
109
width, height = widget.allocation.width, widget.allocation.height
110
value = '%sx%s' % (width, height)
111
self.config.set_user_option(config_name, value)
112
self.connect("destroy", save_size)
114
def set_revision(self, revid):
115
self.treeview.set_revision_id(revid)
117
53
def construct(self):
118
54
"""Construct the window contents."""
119
55
vbox = gtk.VBox(spacing=0)
122
self.paned = gtk.VPaned()
123
self.paned.pack1(self.construct_top(), resize=False, shrink=True)
124
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
127
nav = self.construct_navigation()
128
menubar = self.construct_menubar()
129
vbox.pack_start(menubar, expand=False, fill=True)
130
vbox.pack_start(nav, expand=False, fill=True)
132
vbox.pack_start(self.paned, expand=True, fill=True)
133
vbox.set_focus_child(self.paned)
135
self.treeview.connect('revision-selected',
136
self._treeselection_changed_cb)
137
self.treeview.connect('revision-activated',
138
self._tree_revision_activated)
140
self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
58
vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
61
paned.pack1(self.construct_top(), resize=True, shrink=False)
62
paned.pack2(self.construct_bottom(), resize=False, shrink=True)
64
vbox.pack_start(paned, expand=True, fill=True)
65
vbox.set_focus_child(paned)
143
def construct_menubar(self):
144
menubar = gtk.MenuBar()
146
file_menu = gtk.Menu()
147
file_menuitem = gtk.MenuItem("_File")
148
file_menuitem.set_submenu(file_menu)
150
file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
151
file_menu_close.connect('activate', lambda x: self.destroy())
153
file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
154
file_menu_quit.connect('activate', lambda x: gtk.main_quit())
156
if self._parent is not None:
157
file_menu.add(file_menu_close)
158
file_menu.add(file_menu_quit)
160
edit_menu = gtk.Menu()
161
edit_menuitem = gtk.MenuItem("_Edit")
162
edit_menuitem.set_submenu(edit_menu)
164
edit_menu_branchopts = gtk.MenuItem("Branch Settings")
165
edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
167
edit_menu_globopts = gtk.MenuItem("Global Settings")
168
edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
170
edit_menu.add(edit_menu_branchopts)
171
edit_menu.add(edit_menu_globopts)
173
view_menu = gtk.Menu()
174
view_menuitem = gtk.MenuItem("_View")
175
view_menuitem.set_submenu(view_menu)
177
view_menu_refresh = self.refresh_action.create_menu_item()
178
view_menu_refresh.connect('activate', self._refresh_clicked)
180
view_menu.add(view_menu_refresh)
181
view_menu.add(gtk.SeparatorMenuItem())
183
view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
184
view_menu_toolbar.set_active(True)
185
if self.config.get_user_option('viz-toolbar-visible') == 'False':
186
view_menu_toolbar.set_active(False)
188
view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
190
view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
191
view_menu_compact.set_active(self.compact_view)
192
view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
194
view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
195
view_menu_diffs.set_active(False)
196
if self.config.get_user_option('viz-show-diffs') == 'True':
197
view_menu_diffs.set_active(True)
198
view_menu_diffs.connect('toggled', self._diff_visibility_changed)
200
view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
201
view_menu_wide_diffs.set_active(False)
202
if self.config.get_user_option('viz-wide-diffs') == 'True':
203
view_menu_wide_diffs.set_active(True)
204
view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
206
view_menu_wrap_diffs = gtk.CheckMenuItem("Wrap _Long Lines in Diffs")
207
view_menu_wrap_diffs.set_active(False)
208
if self.config.get_user_option('viz-wrap-diffs') == 'True':
209
view_menu_wrap_diffs.set_active(True)
210
view_menu_wrap_diffs.connect('toggled', self._diff_wrap_changed)
212
view_menu.add(view_menu_toolbar)
213
view_menu.add(view_menu_compact)
214
view_menu.add(gtk.SeparatorMenuItem())
215
view_menu.add(view_menu_diffs)
216
view_menu.add(view_menu_wide_diffs)
217
view_menu.add(view_menu_wrap_diffs)
218
view_menu.add(gtk.SeparatorMenuItem())
220
self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
221
self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
223
# Revision numbers are pointless if there are multiple branches
224
if len(self.start_revs) > 1:
225
self.mnu_show_revno_column.set_sensitive(False)
226
self.treeview.set_property('revno-column-visible', False)
228
for (col, name) in [(self.mnu_show_revno_column, "revno"),
229
(self.mnu_show_date_column, "date")]:
230
col.set_active(self.treeview.get_property(name + "-column-visible"))
231
col.connect('toggled', self._col_visibility_changed, name)
235
go_menu.set_accel_group(self.accel_group)
236
go_menuitem = gtk.MenuItem("_Go")
237
go_menuitem.set_submenu(go_menu)
239
go_menu_next = self.next_rev_action.create_menu_item()
240
go_menu_prev = self.prev_rev_action.create_menu_item()
242
tag_image = gtk.Image()
243
tag_image.set_from_file(icon_path("tag-16.png"))
244
self.go_menu_tags = gtk.ImageMenuItem("_Tags")
245
self.go_menu_tags.set_image(tag_image)
246
self.treeview.connect('refreshed', lambda w: self._update_tags())
248
go_menu.add(go_menu_next)
249
go_menu.add(go_menu_prev)
250
go_menu.add(gtk.SeparatorMenuItem())
251
go_menu.add(self.go_menu_tags)
253
self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
254
revision_menuitem = gtk.MenuItem("_Revision")
255
revision_menuitem.set_submenu(self.revision_menu)
257
branch_menu = gtk.Menu()
258
branch_menuitem = gtk.MenuItem("_Branch")
259
branch_menuitem.set_submenu(branch_menu)
261
branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
262
branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
265
from bzrlib.plugins import search
267
mutter("Didn't find search plugin")
269
branch_menu.add(gtk.SeparatorMenuItem())
271
branch_index_menuitem = gtk.MenuItem("_Index")
272
branch_index_menuitem.connect('activate', self._branch_index_cb)
273
branch_menu.add(branch_index_menuitem)
275
branch_search_menuitem = gtk.MenuItem("_Search")
276
branch_search_menuitem.connect('activate', self._branch_search_cb)
277
branch_menu.add(branch_search_menuitem)
279
help_menu = gtk.Menu()
280
help_menuitem = gtk.MenuItem("_Help")
281
help_menuitem.set_submenu(help_menu)
283
help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
284
help_about_menuitem.connect('activate', self._about_dialog_cb)
286
help_menu.add(help_about_menuitem)
288
menubar.add(file_menuitem)
289
menubar.add(edit_menuitem)
290
menubar.add(view_menuitem)
291
menubar.add(go_menuitem)
292
menubar.add(revision_menuitem)
293
menubar.add(branch_menuitem)
294
menubar.add(help_menuitem)
299
69
def construct_top(self):
300
70
"""Construct the top-half of the window."""
301
# FIXME: Make broken_line_length configurable
303
self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
305
for col in ["revno", "date"]:
306
option = self.config.get_user_option(col + '-column-visible')
307
if option is not None:
308
self.treeview.set_property(col + '-column-visible', option == 'True')
310
self.treeview.set_property(col + '-column-visible', False)
71
scrollwin = gtk.ScrolledWindow()
72
scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
73
scrollwin.set_shadow_type(gtk.SHADOW_IN)
76
self.treeview = gtk.TreeView()
77
self.treeview.set_rules_hint(True)
78
self.treeview.set_search_column(4)
79
self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
80
self.treeview.connect("row-activated", self._treeview_row_activated_cb)
81
scrollwin.add(self.treeview)
312
82
self.treeview.show()
314
align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
315
align.set_padding(5, 0, 0, 0)
316
align.add(self.treeview)
317
# user-configured size
318
size = self._load_size('viz-graph-size')
321
align.set_size_request(width, height)
323
(width, height) = self.get_size()
324
align.set_size_request(width, int(height / 2.5))
325
self._save_size_on_destroy(align, 'viz-graph-size')
84
cell = CellRendererGraph()
85
column = gtk.TreeViewColumn()
86
column.set_resizable(True)
87
column.pack_start(cell, expand=False)
88
column.add_attribute(cell, "node", 1)
89
column.add_attribute(cell, "in-lines", 2)
90
column.add_attribute(cell, "out-lines", 3)
91
self.treeview.append_column(column)
93
cell = gtk.CellRendererText()
94
cell.set_property("width-chars", 40)
95
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
96
column = gtk.TreeViewColumn("Message")
97
column.set_resizable(True)
98
column.pack_start(cell, expand=True)
99
column.add_attribute(cell, "text", 4)
100
self.treeview.append_column(column)
102
cell = gtk.CellRendererText()
103
cell.set_property("width-chars", 40)
104
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
105
column = gtk.TreeViewColumn("Committer")
106
column.set_resizable(True)
107
column.pack_start(cell, expand=True)
108
column.add_attribute(cell, "text", 5)
109
self.treeview.append_column(column)
111
cell = gtk.CellRendererText()
112
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
113
column = gtk.TreeViewColumn("Date")
114
column.set_resizable(True)
115
column.pack_start(cell, expand=True)
116
column.add_attribute(cell, "text", 6)
117
self.treeview.append_column(column)
330
121
def construct_navigation(self):
331
122
"""Construct the navigation buttons."""
332
self.toolbar = gtk.Toolbar()
333
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
335
self.prev_button = self.prev_rev_action.create_tool_item()
336
self.toolbar.insert(self.prev_button, -1)
338
self.next_button = self.next_rev_action.create_tool_item()
339
self.toolbar.insert(self.next_button, -1)
341
self.toolbar.insert(gtk.SeparatorToolItem(), -1)
343
refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
344
refresh_button.connect('clicked', self._refresh_clicked)
345
self.toolbar.insert(refresh_button, -1)
347
self.toolbar.show_all()
124
frame.set_shadow_type(gtk.SHADOW_OUT)
127
hbox = gtk.HBox(spacing=12)
131
self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
132
self.back_button.set_relief(gtk.RELIEF_NONE)
133
self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
135
self.back_button.connect("clicked", self._back_clicked_cb)
136
hbox.pack_start(self.back_button, expand=False, fill=True)
137
self.back_button.show()
139
self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
140
self.fwd_button.set_relief(gtk.RELIEF_NONE)
141
self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
143
self.fwd_button.connect("clicked", self._fwd_clicked_cb)
144
hbox.pack_start(self.fwd_button, expand=False, fill=True)
145
self.fwd_button.show()
351
149
def construct_bottom(self):
352
150
"""Construct the bottom half of the window."""
353
if self.config.get_user_option('viz-wide-diffs') == 'True':
354
self.diff_paned = gtk.VPaned()
356
self.diff_paned = gtk.HPaned()
151
scrollwin = gtk.ScrolledWindow()
152
scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
153
scrollwin.set_shadow_type(gtk.SHADOW_NONE)
357
154
(width, height) = self.get_size()
358
self.diff_paned.set_size_request(20, 20) # shrinkable
360
from bzrlib.plugins.gtk.revisionview import RevisionView
361
self.revisionview = RevisionView(branch=self.branch)
362
self.revisionview.set_size_request(width/3, int(height / 2.5))
363
# user-configured size
364
size = self._load_size('viz-revisionview-size')
367
self.revisionview.set_size_request(width, height)
368
self._save_size_on_destroy(self.revisionview, 'viz-revisionview-size')
369
self.revisionview.show()
370
self.revisionview.set_show_callback(self._show_clicked_cb)
371
self.revisionview.connect('notify::revision', self._go_clicked_cb)
372
self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
373
self.diff_paned.pack1(self.revisionview)
375
from bzrlib.plugins.gtk.diff import DiffWidget
376
self.diff = DiffWidget()
377
self.diff_paned.pack2(self.diff)
379
self.diff_paned.show_all()
380
if self.config.get_user_option('viz-show-diffs') != 'True':
383
return self.diff_paned
385
def _tag_selected_cb(self, menuitem, revid):
386
self.treeview.set_revision_id(revid)
388
def _treeselection_changed_cb(self, selection, *args):
389
"""callback for when the treeview changes."""
390
revision = self.treeview.get_revision()
391
parents = self.treeview.get_parents()
392
children = self.treeview.get_children()
394
self.revision_menu.set_revision_ids([revision.revision_id])
396
if revision and revision != NULL_REVISION:
397
prev_menu = gtk.Menu()
399
self.prev_rev_action.set_sensitive(True)
400
for parent_id in parents:
401
if parent_id and parent_id != NULL_REVISION:
402
parent = self.branch.repository.get_revision(parent_id)
404
str = ' (' + parent.properties['branch-nick'] + ')'
408
item = gtk.MenuItem(parent.message.split("\n")[0] + str)
409
item.connect('activate', self._set_revision_cb, parent_id)
413
self.prev_rev_action.set_sensitive(False)
416
self.prev_button.set_menu(prev_menu)
418
next_menu = gtk.Menu()
419
if len(children) > 0:
420
self.next_rev_action.set_sensitive(True)
421
for child_id in children:
422
child = self.branch.repository.get_revision(child_id)
424
str = ' (' + child.properties['branch-nick'] + ')'
428
item = gtk.MenuItem(child.message.split("\n")[0] + str)
429
item.connect('activate', self._set_revision_cb, child_id)
433
self.next_rev_action.set_sensitive(False)
436
self.next_button.set_menu(next_menu)
438
self.revisionview.set_revision(revision)
439
self.revisionview.set_children(children)
440
self.update_diff_panel(revision, parents)
442
def _tree_revision_activated(self, widget, path, col):
443
# TODO: more than one parent
444
"""Callback for when a treeview row gets activated."""
445
revision = self.treeview.get_revision()
446
parents = self.treeview.get_parents()
448
if len(parents) == 0:
449
parent_id = NULL_REVISION
155
scrollwin.set_size_request(width, int(height / 2.5))
158
vbox = gtk.VBox(False, spacing=6)
159
vbox.set_border_width(6)
160
scrollwin.add_with_viewport(vbox)
163
table = gtk.Table(rows=4, columns=2)
164
table.set_row_spacings(6)
165
table.set_col_spacings(6)
166
vbox.pack_start(table, expand=False, fill=True)
169
align = gtk.Alignment(0.0, 0.5)
171
label.set_markup("<b>Revision:</b>")
173
table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
177
align = gtk.Alignment(0.0, 0.5)
178
self.revid_label = gtk.Label()
179
self.revid_label.set_selectable(True)
180
align.add(self.revid_label)
181
table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
182
self.revid_label.show()
185
align = gtk.Alignment(0.0, 0.5)
187
label.set_markup("<b>Committer:</b>")
189
table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
193
align = gtk.Alignment(0.0, 0.5)
194
self.committer_label = gtk.Label()
195
self.committer_label.set_selectable(True)
196
align.add(self.committer_label)
197
table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
198
self.committer_label.show()
201
align = gtk.Alignment(0.0, 0.5)
203
label.set_markup("<b>Branch nick:</b>")
205
table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
209
align = gtk.Alignment(0.0, 0.5)
210
self.branchnick_label = gtk.Label()
211
self.branchnick_label.set_selectable(True)
212
align.add(self.branchnick_label)
213
table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
214
self.branchnick_label.show()
217
align = gtk.Alignment(0.0, 0.5)
219
label.set_markup("<b>Timestamp:</b>")
221
table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
225
align = gtk.Alignment(0.0, 0.5)
226
self.timestamp_label = gtk.Label()
227
self.timestamp_label.set_selectable(True)
228
align.add(self.timestamp_label)
229
table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
230
self.timestamp_label.show()
233
self.parents_table = gtk.Table(rows=1, columns=2)
234
self.parents_table.set_row_spacings(3)
235
self.parents_table.set_col_spacings(6)
236
self.parents_table.show()
237
vbox.pack_start(self.parents_table, expand=False, fill=True)
238
self.parents_widgets = []
241
label.set_markup("<b>Parents:</b>")
242
align = gtk.Alignment(0.0, 0.5)
244
self.parents_table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
248
self.message_buffer = gtk.TextBuffer()
249
textview = gtk.TextView(self.message_buffer)
250
textview.set_editable(False)
251
textview.set_wrap_mode(gtk.WRAP_WORD)
252
textview.modify_font(pango.FontDescription("Monospace"))
253
vbox.pack_start(textview, expand=True, fill=True)
258
def set_branch(self, branch, start, maxnum):
259
"""Set the branch and start position for this window.
261
Creates a new TreeModel and populates it with information about
262
the new branch before updating the window title and model of the
267
# [ revision, node, last_lines, lines, message, committer, timestamp ]
268
self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
269
gobject.TYPE_PYOBJECT,
270
gobject.TYPE_PYOBJECT,
271
gobject.TYPE_PYOBJECT,
277
(self.revisions, colours, self.children, self.parent_ids,
278
merge_sorted) = distances(branch, start)
279
for (index, (revision, node, lines)) in enumerate(graph(
280
self.revisions, colours, merge_sorted)):
281
# FIXME: at this point we should be able to show the graph order
282
# and lines with no message or commit data - and then incrementally
283
# fill the timestamp, committer etc data as desired.
284
message = revision.message.split("\n")[0]
285
if revision.committer is not None:
286
timestamp = format_date(revision.timestamp, revision.timezone)
289
self.model.append([revision, node, last_lines, lines,
290
message, revision.committer, timestamp])
291
self.index[revision] = index
293
if maxnum is not None and index > maxnum:
296
self.set_title(branch.nick + " - bzrk")
297
self.treeview.set_model(self.model)
299
def _treeview_cursor_cb(self, *args):
300
"""Callback for when the treeview cursor changes."""
301
(path, col) = self.treeview.get_cursor()
302
revision = self.model[path][0]
304
self.back_button.set_sensitive(len(self.parent_ids[revision]) > 0)
305
self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
307
if revision.committer is not None:
309
committer = revision.committer
310
timestamp = format_date(revision.timestamp, revision.timezone)
311
message = revision.message
313
branchnick = revision.properties['branch-nick']
451
parent_id = parents[0]
453
self.show_diff(revision.revision_id, parent_id)
454
self.treeview.grab_focus()
323
self.revid_label.set_text(revision.revision_id)
324
self.branchnick_label.set_text(branchnick)
326
self.committer_label.set_text(committer)
327
self.timestamp_label.set_text(timestamp)
328
self.message_buffer.set_text(message)
330
for widget in self.parents_widgets:
331
self.parents_table.remove(widget)
333
self.parents_widgets = []
334
self.parents_table.resize(max(len(self.parent_ids[revision]), 1), 2)
336
for idx, parent_id in enumerate(self.parent_ids[revision]):
337
align = gtk.Alignment(0.0, 0.0)
338
self.parents_widgets.append(align)
339
self.parents_table.attach(align, 1, 2, idx, idx + 1,
340
gtk.EXPAND | gtk.FILL, gtk.FILL)
343
hbox = gtk.HBox(False, spacing=6)
348
image.set_from_stock(
349
gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_SMALL_TOOLBAR)
352
button = gtk.Button()
354
button.connect("clicked", self._go_clicked_cb, parent_id)
355
hbox.pack_start(button, expand=False, fill=True)
359
image.set_from_stock(
360
gtk.STOCK_FIND, gtk.ICON_SIZE_SMALL_TOOLBAR)
363
button = gtk.Button()
365
button.set_sensitive(self.app is not None)
366
button.connect("clicked", self._show_clicked_cb,
367
revision.revision_id, parent_id)
368
hbox.pack_start(button, expand=False, fill=True)
371
label = gtk.Label(parent_id)
372
label.set_selectable(True)
373
hbox.pack_start(label, expand=False, fill=True)
456
377
def _back_clicked_cb(self, *args):
457
378
"""Callback for when the back button is clicked."""
379
(path, col) = self.treeview.get_cursor()
380
revision = self.model[path][0]
381
if not len(self.parent_ids[revision]):
384
for parent_id in self.parent_ids[revision]:
385
parent = self.revisions[parent_id]
386
if same_branch(revision, parent):
387
self.treeview.set_cursor(self.index[parent])
390
next = self.revisions[self.parent_ids[revision][0]]
391
self.treeview.set_cursor(self.index[next])
392
self.treeview.grab_focus()
460
394
def _fwd_clicked_cb(self, *args):
461
395
"""Callback for when the forward button is clicked."""
462
self.treeview.forward()
464
def _go_clicked_cb(self, w, p):
396
(path, col) = self.treeview.get_cursor()
397
revision = self.model[path][0]
398
if not len(self.children[revision]):
401
for child in self.children[revision]:
402
if same_branch(child, revision):
403
self.treeview.set_cursor(self.index[child])
406
prev = list(self.children[revision])[0]
407
self.treeview.set_cursor(self.index[prev])
408
self.treeview.grab_focus()
410
def _go_clicked_cb(self, widget, revid):
465
411
"""Callback for when the go button for a parent is clicked."""
466
if self.revisionview.get_revision() is not None:
467
self.treeview.set_revision(self.revisionview.get_revision())
412
self.treeview.set_cursor(self.index[self.revisions[revid]])
413
self.treeview.grab_focus()
469
def _show_clicked_cb(self, revid, parentid):
415
def _show_clicked_cb(self, widget, revid, parentid):
470
416
"""Callback for when the show button for a parent is clicked."""
471
self.show_diff(revid, parentid)
472
self.treeview.grab_focus()
474
def _set_revision_cb(self, w, revision_id):
475
self.treeview.set_revision_id(revision_id)
477
def _brokenlines_toggled_cb(self, button):
478
self.compact_view = button.get_active()
480
if self.compact_view:
485
self.config.set_user_option('viz-compact-view', option)
486
self.treeview.set_property('compact', self.compact_view)
487
self.treeview.refresh()
489
def _branch_index_cb(self, w):
490
from bzrlib.plugins.search import index as _mod_index
491
_mod_index.index_url(self.branch.base)
493
def _branch_search_cb(self, w):
494
from bzrlib.plugins.search import index as _mod_index
495
from bzrlib.plugins.gtk.search import SearchDialog
496
from bzrlib.plugins.search import errors as search_errors
499
index = _mod_index.open_index_url(self.branch.base)
500
except search_errors.NoSearchIndex:
501
dialog = gtk.MessageDialog(self, type=gtk.MESSAGE_QUESTION,
502
buttons=gtk.BUTTONS_OK_CANCEL,
503
message_format="This branch has not been indexed yet. "
505
if dialog.run() == gtk.RESPONSE_OK:
507
index = _mod_index.index_url(self.branch.base)
512
dialog = SearchDialog(index)
514
if dialog.run() == gtk.RESPONSE_OK:
515
self.set_revision(dialog.get_revision())
519
def _about_dialog_cb(self, w):
520
from bzrlib.plugins.gtk.about import AboutDialog
524
def _col_visibility_changed(self, col, property):
525
self.config.set_user_option(property + '-column-visible', col.get_active())
526
self.treeview.set_property(property + '-column-visible', col.get_active())
528
def _toolbar_visibility_changed(self, col):
533
self.config.set_user_option('viz-toolbar-visible', col.get_active())
535
def _make_diff_nonzero_size(self):
536
"""make sure the diff isn't zero-width or zero-height"""
537
alloc = self.diff.get_allocation()
538
if (alloc.width < 10) or (alloc.height < 10):
539
width, height = self.get_size()
540
self.revisionview.set_size_request(width/3, int(height / 2.5))
542
def _diff_visibility_changed(self, col):
543
"""Hide or show the diff panel."""
546
self._make_diff_nonzero_size()
549
self.config.set_user_option('viz-show-diffs', str(col.get_active()))
550
self.update_diff_panel()
552
def _diff_placement_changed(self, col):
553
"""Toggle the diff panel's position."""
554
self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
556
old = self.paned.get_child2()
557
self.paned.remove(old)
558
self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
559
self._make_diff_nonzero_size()
561
self.treeview.emit('revision-selected')
563
def _diff_wrap_changed(self, widget):
564
"""Toggle word wrap in the diff widget."""
565
self.config.set_user_option('viz-wrap-diffs', widget.get_active())
566
self.diff._on_wraplines_toggled(widget)
568
def _show_about_cb(self, w):
569
dialog = AboutDialog()
570
dialog.connect('response', lambda d,r: d.destroy())
573
def _refresh_clicked(self, w):
574
self.treeview.refresh()
576
def _update_tags(self):
579
if self.branch.supports_tags():
580
tags = self.branch.tags.get_tag_dict().items()
581
tags.sort(reverse=True)
582
for tag, revid in tags:
583
tag_image = gtk.Image()
584
tag_image.set_from_file(icon_path('tag-16.png'))
585
tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
586
tag_item.set_image(tag_image)
587
tag_item.connect('activate', self._tag_selected_cb, revid)
588
tag_item.set_sensitive(self.treeview.has_revision_id(revid))
590
self.go_menu_tags.set_submenu(menu)
592
self.go_menu_tags.set_sensitive(len(tags) != 0)
594
self.go_menu_tags.set_sensitive(False)
596
self.go_menu_tags.show_all()
598
def _load_size(self, name):
599
"""Read and parse 'name' from self.config.
600
The value is a string, formatted as WIDTHxHEIGHT
601
Returns None, or (width, height)
603
size = self.config.get_user_option(name)
605
width, height = [int(num) for num in size.split('x')]
606
# avoid writing config every time we start
610
def show_diff(self, revid=None, parentid=NULL_REVISION):
611
"""Open a new window to show a diff between the given revisions."""
612
from bzrlib.plugins.gtk.diff import DiffWindow
613
window = DiffWindow(parent=self)
615
rev_tree = self.branch.repository.revision_tree(revid)
616
parent_tree = self.branch.repository.revision_tree(parentid)
618
description = revid + " - " + self.branch._get_nick(local=True)
619
window.set_diff(description, rev_tree, parent_tree)
622
def update_diff_panel(self, revision=None, parents=None):
623
"""Show the current revision in the diff panel."""
624
if self.config.get_user_option('viz-show-diffs') != 'True':
627
if not revision: # default to selected row
628
revision = self.treeview.get_revision()
629
if revision == NULL_REVISION:
632
if not parents: # default to selected row's parents
633
parents = self.treeview.get_parents()
634
if len(parents) == 0:
635
parent_id = NULL_REVISION
637
parent_id = parents[0]
639
rev_tree = self.branch.repository.revision_tree(revision.revision_id)
640
parent_tree = self.branch.repository.revision_tree(parent_id)
642
self.diff.set_diff(rev_tree, parent_tree)
643
if self.config.get_user_option('viz-wrap-diffs') == 'True':
644
self.diff._on_wraplines_toggled(wrap=True)
417
if self.app is not None:
418
self.app.show_diff(self.branch, revid, parentid)
419
self.treeview.grab_focus()
421
def _treeview_row_activated_cb(self, widget, path, col):
422
# TODO: more than one parent
423
"""Callback for when a treeview row gets activated."""
424
revision = self.model[path][0]
425
parent_id = self.parent_ids[revision][0]
426
if self.app is not None:
427
self.app.show_diff(self.branch, revision.revision_id, parent_id)
428
self.treeview.grab_focus()