/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to viz/branchwin.py

  • Committer: Jasper Groenewegen
  • Date: 2008-07-22 19:46:47 UTC
  • mfrom: (560.2.1 LP151818-no-push-Q)
  • Revision ID: colbrac@xs4all.nl-20080722194647-25sbnnkj9wdt2v00
Merge removal of question to set default push location

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: UTF-8 -*-
 
2
"""Branch window.
 
3
 
 
4
This module contains the code to manage the branch information window,
 
5
which contains both the revision graph and details panes.
 
6
"""
 
7
 
 
8
__copyright__ = "Copyright © 2005 Canonical Ltd."
 
9
__author__    = "Scott James Remnant <scott@ubuntu.com>"
 
10
 
 
11
 
 
12
import gtk
 
13
import gobject
 
14
import pango
 
15
 
 
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
 
22
 
 
23
from bzrlib.config import BranchConfig, GlobalConfig
 
24
from bzrlib.revision import Revision, NULL_REVISION
 
25
from bzrlib.trace import mutter
 
26
 
 
27
class BranchWindow(Window):
 
28
    """Branch window.
 
29
 
 
30
    This object represents and manages a single window containing information
 
31
    for a particular branch.
 
32
    """
 
33
 
 
34
    def __init__(self, branch, start_revs, maxnum, parent=None):
 
35
        """Create a new BranchWindow.
 
36
 
 
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, 
 
40
                       None for no limit.
 
41
        """
 
42
 
 
43
        Window.__init__(self, parent=parent)
 
44
        self.set_border_width(0)
 
45
 
 
46
        self.branch      = branch
 
47
        self.start_revs  = start_revs
 
48
        self.maxnum      = maxnum
 
49
        self.config      = GlobalConfig()
 
50
 
 
51
        self._sizes      = {} # window and widget sizes
 
52
 
 
53
        if self.config.get_user_option('viz-compact-view') == 'yes':
 
54
            self.compact_view = True
 
55
        else:
 
56
            self.compact_view = False
 
57
 
 
58
        self.set_title(branch.nick + " - revision history")
 
59
 
 
60
        # Use three-quarters of the screen by default
 
61
        screen = self.get_screen()
 
62
        monitor = screen.get_monitor_geometry(0)
 
63
        width = int(monitor.width * 0.75)
 
64
        height = int(monitor.height * 0.75)
 
65
        # user-configured window size
 
66
        size = self._load_size('viz-window-size')
 
67
        if size:
 
68
            width, height = size
 
69
        self.set_default_size(width, height)
 
70
        self.set_size_request(width/3, height/3)
 
71
        self.connect("size-allocate", self._on_size_allocate, 'viz-window-size')
 
72
 
 
73
        # FIXME AndyFitz!
 
74
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
 
75
        self.set_icon(icon)
 
76
 
 
77
        gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
 
78
        gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
 
79
        gtk.accel_map_add_entry("<viz>/View/Refresh", gtk.keysyms.F5, 0)
 
80
 
 
81
        self.accel_group = gtk.AccelGroup()
 
82
        self.add_accel_group(self.accel_group)
 
83
 
 
84
        gtk.Action.set_tool_item_type(gtk.MenuToolButton)
 
85
 
 
86
        self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
 
87
        self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
 
88
        self.prev_rev_action.set_accel_group(self.accel_group)
 
89
        self.prev_rev_action.connect("activate", self._back_clicked_cb)
 
90
        self.prev_rev_action.connect_accelerator()
 
91
 
 
92
        self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
 
93
        self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
 
94
        self.next_rev_action.set_accel_group(self.accel_group)
 
95
        self.next_rev_action.connect("activate", self._fwd_clicked_cb)
 
96
        self.next_rev_action.connect_accelerator()
 
97
 
 
98
        self.refresh_action = gtk.Action("refresh", "_Refresh", "Refresh view", gtk.STOCK_REFRESH)
 
99
        self.refresh_action.set_accel_path("<viz>/View/Refresh")
 
100
        self.refresh_action.set_accel_group(self.accel_group)
 
101
        self.refresh_action.connect("activate", self._refresh_clicked)
 
102
        self.refresh_action.connect_accelerator()
 
103
 
 
104
        self.construct()
 
105
 
 
106
    def set_revision(self, revid):
 
107
        self.treeview.set_revision_id(revid)
 
108
 
 
109
    def construct(self):
 
110
        """Construct the window contents."""
 
111
        vbox = gtk.VBox(spacing=0)
 
112
        self.add(vbox)
 
113
 
 
114
        self.paned = gtk.VPaned()
 
115
        self.paned.pack1(self.construct_top(), resize=False, shrink=True)
 
116
        self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
 
117
        self.paned.show()
 
118
 
 
119
        nav = self.construct_navigation()
 
120
        menubar = self.construct_menubar()
 
121
        vbox.pack_start(menubar, expand=False, fill=True)
 
122
        vbox.pack_start(nav, expand=False, fill=True)
 
123
 
 
124
        vbox.pack_start(self.paned, expand=True, fill=True)
 
125
        vbox.set_focus_child(self.paned)
 
126
 
 
127
        vbox.show()
 
128
 
 
129
    def construct_menubar(self):
 
130
        menubar = gtk.MenuBar()
 
131
 
 
132
        file_menu = gtk.Menu()
 
133
        file_menuitem = gtk.MenuItem("_File")
 
134
        file_menuitem.set_submenu(file_menu)
 
135
 
 
136
        file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
 
137
        file_menu_close.connect('activate', lambda x: self.destroy())
 
138
        
 
139
        file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
 
140
        file_menu_quit.connect('activate', lambda x: gtk.main_quit())
 
141
        
 
142
        if self._parent is not None:
 
143
            file_menu.add(file_menu_close)
 
144
        file_menu.add(file_menu_quit)
 
145
 
 
146
        edit_menu = gtk.Menu()
 
147
        edit_menuitem = gtk.MenuItem("_Edit")
 
148
        edit_menuitem.set_submenu(edit_menu)
 
149
 
 
150
        edit_menu_branchopts = gtk.MenuItem("Branch Settings")
 
151
        edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
 
152
 
 
153
        edit_menu_globopts = gtk.MenuItem("Global Settings")
 
154
        edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
 
155
 
 
156
        edit_menu.add(edit_menu_branchopts)
 
157
        edit_menu.add(edit_menu_globopts)
 
158
 
 
159
        view_menu = gtk.Menu()
 
160
        view_menuitem = gtk.MenuItem("_View")
 
161
        view_menuitem.set_submenu(view_menu)
 
162
 
 
163
        view_menu_refresh = self.refresh_action.create_menu_item()
 
164
        view_menu_refresh.connect('activate', self._refresh_clicked)
 
165
 
 
166
        view_menu.add(view_menu_refresh)
 
167
        view_menu.add(gtk.SeparatorMenuItem())
 
168
 
 
169
        view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
 
170
        view_menu_toolbar.set_active(True)
 
171
        if self.config.get_user_option('viz-toolbar-visible') == 'False':
 
172
            view_menu_toolbar.set_active(False)
 
173
            self.toolbar.hide()
 
174
        view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
 
175
 
 
176
        view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
 
177
        view_menu_compact.set_active(self.compact_view)
 
178
        view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
 
179
 
 
180
        view_menu_diffs = gtk.CheckMenuItem("Show Diffs")
 
181
        view_menu_diffs.set_active(False)
 
182
        if self.config.get_user_option('viz-show-diffs') == 'True':
 
183
            view_menu_diffs.set_active(True)
 
184
        view_menu_diffs.connect('toggled', self._diff_visibility_changed)
 
185
 
 
186
        view_menu_wide_diffs = gtk.CheckMenuItem("Wide Diffs")
 
187
        view_menu_wide_diffs.set_active(False)
 
188
        if self.config.get_user_option('viz-wide-diffs') == 'True':
 
189
            view_menu_wide_diffs.set_active(True)
 
190
        view_menu_wide_diffs.connect('toggled', self._diff_placement_changed)
 
191
 
 
192
        view_menu.add(view_menu_toolbar)
 
193
        view_menu.add(view_menu_compact)
 
194
        view_menu.add(view_menu_diffs)
 
195
        view_menu.add(view_menu_wide_diffs)
 
196
        view_menu.add(gtk.SeparatorMenuItem())
 
197
 
 
198
        self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
 
199
        self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
 
200
 
 
201
        # Revision numbers are pointless if there are multiple branches
 
202
        if len(self.start_revs) > 1:
 
203
            self.mnu_show_revno_column.set_sensitive(False)
 
204
            self.treeview.set_property('revno-column-visible', False)
 
205
 
 
206
        for (col, name) in [(self.mnu_show_revno_column, "revno"), 
 
207
                            (self.mnu_show_date_column, "date")]:
 
208
            col.set_active(self.treeview.get_property(name + "-column-visible"))
 
209
            col.connect('toggled', self._col_visibility_changed, name)
 
210
            view_menu.add(col)
 
211
 
 
212
        go_menu = gtk.Menu()
 
213
        go_menu.set_accel_group(self.accel_group)
 
214
        go_menuitem = gtk.MenuItem("_Go")
 
215
        go_menuitem.set_submenu(go_menu)
 
216
        
 
217
        go_menu_next = self.next_rev_action.create_menu_item()
 
218
        go_menu_prev = self.prev_rev_action.create_menu_item()
 
219
 
 
220
        tag_image = gtk.Image()
 
221
        tag_image.set_from_file(icon_path("tag-16.png"))
 
222
        self.go_menu_tags = gtk.ImageMenuItem("_Tags")
 
223
        self.go_menu_tags.set_image(tag_image)
 
224
        self._update_tags()
 
225
 
 
226
        go_menu.add(go_menu_next)
 
227
        go_menu.add(go_menu_prev)
 
228
        go_menu.add(gtk.SeparatorMenuItem())
 
229
        go_menu.add(self.go_menu_tags)
 
230
 
 
231
        self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
 
232
        revision_menuitem = gtk.MenuItem("_Revision")
 
233
        revision_menuitem.set_submenu(self.revision_menu)
 
234
 
 
235
        branch_menu = gtk.Menu()
 
236
        branch_menuitem = gtk.MenuItem("_Branch")
 
237
        branch_menuitem.set_submenu(branch_menu)
 
238
 
 
239
        branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
 
240
        branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
 
241
 
 
242
        try:
 
243
            from bzrlib.plugins import search
 
244
        except ImportError:
 
245
            mutter("Didn't find search plugin")
 
246
        else:
 
247
            branch_menu.add(gtk.SeparatorMenuItem())
 
248
 
 
249
            branch_index_menuitem = gtk.MenuItem("_Index")
 
250
            branch_index_menuitem.connect('activate', self._branch_index_cb)
 
251
            branch_menu.add(branch_index_menuitem)
 
252
 
 
253
            branch_search_menuitem = gtk.MenuItem("_Search")
 
254
            branch_search_menuitem.connect('activate', self._branch_search_cb)
 
255
            branch_menu.add(branch_search_menuitem)
 
256
 
 
257
        help_menu = gtk.Menu()
 
258
        help_menuitem = gtk.MenuItem("_Help")
 
259
        help_menuitem.set_submenu(help_menu)
 
260
 
 
261
        help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
 
262
        help_about_menuitem.connect('activate', self._about_dialog_cb)
 
263
 
 
264
        help_menu.add(help_about_menuitem)
 
265
 
 
266
        menubar.add(file_menuitem)
 
267
        menubar.add(edit_menuitem)
 
268
        menubar.add(view_menuitem)
 
269
        menubar.add(go_menuitem)
 
270
        menubar.add(revision_menuitem)
 
271
        menubar.add(branch_menuitem)
 
272
        menubar.add(help_menuitem)
 
273
        menubar.show_all()
 
274
 
 
275
        return menubar
 
276
 
 
277
    def construct_top(self):
 
278
        """Construct the top-half of the window."""
 
279
        # FIXME: Make broken_line_length configurable
 
280
 
 
281
        self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
 
282
 
 
283
        self.treeview.connect('revision-selected',
 
284
                self._treeselection_changed_cb)
 
285
        self.treeview.connect('revision-activated',
 
286
                self._tree_revision_activated)
 
287
 
 
288
        self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
 
289
 
 
290
        for col in ["revno", "date"]:
 
291
            option = self.config.get_user_option(col + '-column-visible')
 
292
            if option is not None:
 
293
                self.treeview.set_property(col + '-column-visible', option == 'True')
 
294
            else:
 
295
                self.treeview.set_property(col + '-column-visible', False)
 
296
 
 
297
        self.treeview.show()
 
298
 
 
299
        align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
 
300
        align.set_padding(5, 0, 0, 0)
 
301
        align.add(self.treeview)
 
302
        # user-configured size
 
303
        size = self._load_size('viz-graph-size')
 
304
        if size:
 
305
            width, height = size
 
306
            align.set_size_request(width, height)
 
307
        else:
 
308
            (width, height) = self.get_size()
 
309
            align.set_size_request(width, int(height / 2.5))
 
310
        align.connect('size-allocate', self._on_size_allocate, 'viz-graph-size')
 
311
        align.show()
 
312
 
 
313
        return align
 
314
 
 
315
    def construct_navigation(self):
 
316
        """Construct the navigation buttons."""
 
317
        self.toolbar = gtk.Toolbar()
 
318
        self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
 
319
 
 
320
        self.prev_button = self.prev_rev_action.create_tool_item()
 
321
        self.toolbar.insert(self.prev_button, -1)
 
322
 
 
323
        self.next_button = self.next_rev_action.create_tool_item()
 
324
        self.toolbar.insert(self.next_button, -1)
 
325
 
 
326
        self.toolbar.insert(gtk.SeparatorToolItem(), -1)
 
327
 
 
328
        refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
 
329
        refresh_button.connect('clicked', self._refresh_clicked)
 
330
        self.toolbar.insert(refresh_button, -1)
 
331
 
 
332
        self.toolbar.show_all()
 
333
 
 
334
        return self.toolbar
 
335
 
 
336
    def construct_bottom(self):
 
337
        """Construct the bottom half of the window."""
 
338
        if self.config.get_user_option('viz-wide-diffs') == 'True':
 
339
            self.diff_paned = gtk.VPaned()
 
340
        else:
 
341
            self.diff_paned = gtk.HPaned()
 
342
        (width, height) = self.get_size()
 
343
        self.diff_paned.set_size_request(20, 20) # shrinkable
 
344
 
 
345
        from bzrlib.plugins.gtk.revisionview import RevisionView
 
346
        self.revisionview = RevisionView(branch=self.branch)
 
347
        self.revisionview.set_size_request(width/3, int(height / 2.5))
 
348
        # user-configured size
 
349
        size = self._load_size('viz-revisionview-size')
 
350
        if size:
 
351
            width, height = size
 
352
            self.revisionview.set_size_request(width, height)
 
353
        self.revisionview.connect('size-allocate', self._on_size_allocate, 'viz-revisionview-size')
 
354
        self.revisionview.show()
 
355
        self.revisionview.set_show_callback(self._show_clicked_cb)
 
356
        self.revisionview.connect('notify::revision', self._go_clicked_cb)
 
357
        self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
 
358
        self.diff_paned.pack1(self.revisionview)
 
359
 
 
360
        from bzrlib.plugins.gtk.diff import DiffWidget
 
361
        self.diff = DiffWidget()
 
362
        self.diff_paned.pack2(self.diff)
 
363
 
 
364
        self.diff_paned.show_all()
 
365
        if self.config.get_user_option('viz-show-diffs') != 'True':
 
366
            self.diff.hide()
 
367
 
 
368
        return self.diff_paned
 
369
 
 
370
    def _tag_selected_cb(self, menuitem, revid):
 
371
        self.treeview.set_revision_id(revid)
 
372
 
 
373
    def _treeselection_changed_cb(self, selection, *args):
 
374
        """callback for when the treeview changes."""
 
375
        revision = self.treeview.get_revision()
 
376
        parents  = self.treeview.get_parents()
 
377
        children = self.treeview.get_children()
 
378
 
 
379
        self.revision_menu.set_revision_ids([revision.revision_id])
 
380
 
 
381
        if revision and revision != NULL_REVISION:
 
382
            prev_menu = gtk.Menu()
 
383
            if len(parents) > 0:
 
384
                self.prev_rev_action.set_sensitive(True)
 
385
                for parent_id in parents:
 
386
                    if parent_id and parent_id != NULL_REVISION:
 
387
                        parent = self.branch.repository.get_revision(parent_id)
 
388
                        try:
 
389
                            str = ' (' + parent.properties['branch-nick'] + ')'
 
390
                        except KeyError:
 
391
                            str = ""
 
392
 
 
393
                        item = gtk.MenuItem(parent.message.split("\n")[0] + str)
 
394
                        item.connect('activate', self._set_revision_cb, parent_id)
 
395
                        prev_menu.add(item)
 
396
                prev_menu.show_all()
 
397
            else:
 
398
                self.prev_rev_action.set_sensitive(False)
 
399
                prev_menu.hide()
 
400
 
 
401
            self.prev_button.set_menu(prev_menu)
 
402
 
 
403
            next_menu = gtk.Menu()
 
404
            if len(children) > 0:
 
405
                self.next_rev_action.set_sensitive(True)
 
406
                for child_id in children:
 
407
                    child = self.branch.repository.get_revision(child_id)
 
408
                    try:
 
409
                        str = ' (' + child.properties['branch-nick'] + ')'
 
410
                    except KeyError:
 
411
                        str = ""
 
412
 
 
413
                    item = gtk.MenuItem(child.message.split("\n")[0] + str)
 
414
                    item.connect('activate', self._set_revision_cb, child_id)
 
415
                    next_menu.add(item)
 
416
                next_menu.show_all()
 
417
            else:
 
418
                self.next_rev_action.set_sensitive(False)
 
419
                next_menu.hide()
 
420
 
 
421
            self.next_button.set_menu(next_menu)
 
422
 
 
423
            self.revisionview.set_revision(revision)
 
424
            self.revisionview.set_children(children)
 
425
 
 
426
            self.update_diff_panel(revision, parents)
 
427
 
 
428
    def _tree_revision_activated(self, widget, path, col):
 
429
        # TODO: more than one parent
 
430
        """Callback for when a treeview row gets activated."""
 
431
        revision = self.treeview.get_revision()
 
432
        parents  = self.treeview.get_parents()
 
433
 
 
434
        if len(parents) == 0:
 
435
            parent_id = None
 
436
        else:
 
437
            parent_id = parents[0]
 
438
 
 
439
        self.show_diff(revision.revision_id, parent_id)
 
440
        self.treeview.grab_focus()
 
441
        
 
442
    def _back_clicked_cb(self, *args):
 
443
        """Callback for when the back button is clicked."""
 
444
        self.treeview.back()
 
445
        
 
446
    def _fwd_clicked_cb(self, *args):
 
447
        """Callback for when the forward button is clicked."""
 
448
        self.treeview.forward()
 
449
 
 
450
    def _go_clicked_cb(self, w, p):
 
451
        """Callback for when the go button for a parent is clicked."""
 
452
        if self.revisionview.get_revision() is not None:
 
453
            self.treeview.set_revision(self.revisionview.get_revision())
 
454
 
 
455
    def _show_clicked_cb(self, revid, parentid):
 
456
        """Callback for when the show button for a parent is clicked."""
 
457
        self.show_diff(revid, parentid)
 
458
        self.treeview.grab_focus()
 
459
 
 
460
    def _set_revision_cb(self, w, revision_id):
 
461
        self.treeview.set_revision_id(revision_id)
 
462
 
 
463
    def _brokenlines_toggled_cb(self, button):
 
464
        self.compact_view = button.get_active()
 
465
 
 
466
        if self.compact_view:
 
467
            option = 'yes'
 
468
        else:
 
469
            option = 'no'
 
470
 
 
471
        self.config.set_user_option('viz-compact-view', option)
 
472
        self.treeview.set_property('compact', self.compact_view)
 
473
        self.treeview.refresh()
 
474
 
 
475
    def _branch_index_cb(self, w):
 
476
        from bzrlib.plugins.search import index as _mod_index
 
477
        _mod_index.index_url(self.branch.base)
 
478
 
 
479
    def _branch_search_cb(self, w):
 
480
        from bzrlib.plugins.search import index as _mod_index
 
481
        from bzrlib.plugins.gtk.search import SearchDialog
 
482
        from bzrlib.plugins.search import errors as search_errors
 
483
 
 
484
        try:
 
485
            index = _mod_index.open_index_url(self.branch.base)
 
486
        except search_errors.NoSearchIndex:
 
487
            dialog = gtk.MessageDialog(self, type=gtk.MESSAGE_QUESTION, 
 
488
                buttons=gtk.BUTTONS_OK_CANCEL, 
 
489
                message_format="This branch has not been indexed yet. "
 
490
                               "Index now?")
 
491
            if dialog.run() == gtk.RESPONSE_OK:
 
492
                dialog.destroy()
 
493
                index = _mod_index.index_url(self.branch.base)
 
494
            else:
 
495
                dialog.destroy()
 
496
                return
 
497
 
 
498
        dialog = SearchDialog(index)
 
499
        
 
500
        if dialog.run() == gtk.RESPONSE_OK:
 
501
            self.set_revision(dialog.get_revision())
 
502
 
 
503
        dialog.destroy()
 
504
 
 
505
    def _about_dialog_cb(self, w):
 
506
        from bzrlib.plugins.gtk.about import AboutDialog
 
507
 
 
508
        AboutDialog().run()
 
509
 
 
510
    def _col_visibility_changed(self, col, property):
 
511
        self.config.set_user_option(property + '-column-visible', col.get_active())
 
512
        self.treeview.set_property(property + '-column-visible', col.get_active())
 
513
 
 
514
    def _toolbar_visibility_changed(self, col):
 
515
        if col.get_active():
 
516
            self.toolbar.show()
 
517
        else:
 
518
            self.toolbar.hide()
 
519
        self.config.set_user_option('viz-toolbar-visible', col.get_active())
 
520
 
 
521
    def _make_diff_nonzero_size(self):
 
522
        """make sure the diff isn't zero-width or zero-height"""
 
523
        alloc = self.diff.get_allocation()
 
524
        if (alloc.width < 10) or (alloc.height < 10):
 
525
            width, height = self.get_size()
 
526
            self.revisionview.set_size_request(width/3, int(height / 2.5))
 
527
 
 
528
    def _diff_visibility_changed(self, col):
 
529
        """Hide or show the diff panel."""
 
530
        if col.get_active():
 
531
            self.diff.show()
 
532
            self._make_diff_nonzero_size()
 
533
        else:
 
534
            self.diff.hide()
 
535
        self.config.set_user_option('viz-show-diffs', str(col.get_active()))
 
536
        self.update_diff_panel()
 
537
 
 
538
    def _diff_placement_changed(self, col):
 
539
        """Toggle the diff panel's position."""
 
540
        self.config.set_user_option('viz-wide-diffs', str(col.get_active()))
 
541
 
 
542
        old = self.paned.get_child2()
 
543
        self.paned.remove(old)
 
544
        self.paned.pack2(self.construct_bottom(), resize=True, shrink=False)
 
545
        self._make_diff_nonzero_size()
 
546
 
 
547
        self.treeview.emit('revision-selected')
 
548
 
 
549
    def _show_about_cb(self, w):
 
550
        dialog = AboutDialog()
 
551
        dialog.connect('response', lambda d,r: d.destroy())
 
552
        dialog.run()
 
553
 
 
554
    def _refresh_clicked(self, w):
 
555
        self.treeview.refresh()
 
556
 
 
557
    def _update_tags(self):
 
558
        menu = gtk.Menu()
 
559
 
 
560
        if self.branch.supports_tags():
 
561
            tags = self.branch.tags.get_tag_dict().items()
 
562
            tags.sort()
 
563
            tags.reverse()
 
564
            for tag, revid in tags:
 
565
                tag_image = gtk.Image()
 
566
                tag_image.set_from_file(icon_path('tag-16.png'))
 
567
                tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
 
568
                tag_item.set_image(tag_image)
 
569
                tag_item.connect('activate', self._tag_selected_cb, revid)
 
570
                menu.add(tag_item)
 
571
            self.go_menu_tags.set_submenu(menu)
 
572
 
 
573
            self.go_menu_tags.set_sensitive(len(tags) != 0)
 
574
        else:
 
575
            self.go_menu_tags.set_sensitive(False)
 
576
 
 
577
        self.go_menu_tags.show_all()
 
578
 
 
579
    def _load_size(self, name):
 
580
        """Read and parse 'name' from self.config.
 
581
        The value is a string, formatted as WIDTHxHEIGHT
 
582
        Returns None, or (width, height)
 
583
        """
 
584
        size = self.config.get_user_option(name)
 
585
        if size:
 
586
            width, height = [int(num) for num in size.split('x')]
 
587
            # avoid writing config every time we start
 
588
            self._sizes[name] = (width, height)
 
589
            return width, height
 
590
        return None
 
591
 
 
592
    def _on_size_allocate(self, widget, allocation, name):
 
593
        """When window has been resized, save the new size."""
 
594
        width, height = 0, 0
 
595
        if name in self._sizes:
 
596
            width, height = self._sizes[name]
 
597
 
 
598
        size_changed = (width != allocation.width) or \
 
599
                (height != allocation.height)
 
600
 
 
601
        if size_changed:
 
602
            width, height = allocation.width, allocation.height
 
603
            self._sizes[name] = (width, height)
 
604
            value = '%sx%s' % (width, height)
 
605
            self.config.set_user_option(name, value)
 
606
 
 
607
    def show_diff(self, revid=None, parentid=None):
 
608
        """Open a new window to show a diff between the given revisions."""
 
609
        from bzrlib.plugins.gtk.diff import DiffWindow
 
610
        window = DiffWindow(parent=self)
 
611
 
 
612
        if parentid is None:
 
613
            parentid = NULL_REVISION
 
614
 
 
615
        rev_tree    = self.branch.repository.revision_tree(revid)
 
616
        parent_tree = self.branch.repository.revision_tree(parentid)
 
617
 
 
618
        description = revid + " - " + self.branch.nick
 
619
        window.set_diff(description, rev_tree, parent_tree)
 
620
        window.show()
 
621
 
 
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':
 
625
            return
 
626
 
 
627
        if not revision: # default to selected row
 
628
            revision = self.treeview.get_revision()
 
629
        if (not revision) or (revision == NULL_REVISION):
 
630
            return
 
631
 
 
632
        if not parents: # default to selected row's parents
 
633
            parents  = self.treeview.get_parents()
 
634
        if len(parents) == 0:
 
635
            parent_id = None
 
636
        else:
 
637
            parent_id = parents[0]
 
638
 
 
639
        rev_tree    = self.branch.repository.revision_tree(revision.revision_id)
 
640
        parent_tree = self.branch.repository.revision_tree(parent_id)
 
641
 
 
642
        self.diff.set_diff(rev_tree, parent_tree)
 
643
        self.diff.show_all()