/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: Jelmer Vernooij
  • Date: 2008-07-17 11:51:03 UTC
  • Revision ID: jelmer@samba.org-20080717115103-djh5sb0pvpse2zkb
Add note about glade.

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
        if self.config.get_user_option('viz-compact-view') == 'yes':
 
52
            self.compact_view = True
 
53
        else:
 
54
            self.compact_view = False
 
55
 
 
56
        self.set_title(branch.nick + " - revision history")
 
57
 
 
58
        # Use three-quarters of the screen by default
 
59
        screen = self.get_screen()
 
60
        monitor = screen.get_monitor_geometry(0)
 
61
        width = int(monitor.width * 0.75)
 
62
        height = int(monitor.height * 0.75)
 
63
        self.set_default_size(width, height)
 
64
 
 
65
        # FIXME AndyFitz!
 
66
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
 
67
        self.set_icon(icon)
 
68
 
 
69
        gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
 
70
        gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
 
71
        gtk.accel_map_add_entry("<viz>/View/Refresh", gtk.keysyms.F5, 0)
 
72
 
 
73
        self.accel_group = gtk.AccelGroup()
 
74
        self.add_accel_group(self.accel_group)
 
75
 
 
76
        gtk.Action.set_tool_item_type(gtk.MenuToolButton)
 
77
 
 
78
        self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
 
79
        self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
 
80
        self.prev_rev_action.set_accel_group(self.accel_group)
 
81
        self.prev_rev_action.connect("activate", self._back_clicked_cb)
 
82
        self.prev_rev_action.connect_accelerator()
 
83
 
 
84
        self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
 
85
        self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
 
86
        self.next_rev_action.set_accel_group(self.accel_group)
 
87
        self.next_rev_action.connect("activate", self._fwd_clicked_cb)
 
88
        self.next_rev_action.connect_accelerator()
 
89
 
 
90
        self.refresh_action = gtk.Action("refresh", "_Refresh", "Refresh view", gtk.STOCK_REFRESH)
 
91
        self.refresh_action.set_accel_path("<viz>/View/Refresh")
 
92
        self.refresh_action.set_accel_group(self.accel_group)
 
93
        self.refresh_action.connect("activate", self._refresh_clicked)
 
94
        self.refresh_action.connect_accelerator()
 
95
 
 
96
        self.construct()
 
97
 
 
98
    def set_revision(self, revid):
 
99
        self.treeview.set_revision_id(revid)
 
100
 
 
101
    def construct(self):
 
102
        """Construct the window contents."""
 
103
        vbox = gtk.VBox(spacing=0)
 
104
        self.add(vbox)
 
105
 
 
106
        self.paned = gtk.VPaned()
 
107
        self.paned.pack1(self.construct_top(), resize=True, shrink=False)
 
108
        self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
 
109
        self.paned.show()
 
110
 
 
111
        vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
 
112
        vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
 
113
        
 
114
        vbox.pack_start(self.paned, expand=True, fill=True)
 
115
        vbox.set_focus_child(self.paned)
 
116
 
 
117
        vbox.show()
 
118
 
 
119
    def construct_menubar(self):
 
120
        menubar = gtk.MenuBar()
 
121
 
 
122
        file_menu = gtk.Menu()
 
123
        file_menuitem = gtk.MenuItem("_File")
 
124
        file_menuitem.set_submenu(file_menu)
 
125
 
 
126
        file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
 
127
        file_menu_close.connect('activate', lambda x: self.destroy())
 
128
        
 
129
        file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
 
130
        file_menu_quit.connect('activate', lambda x: gtk.main_quit())
 
131
        
 
132
        if self._parent is not None:
 
133
            file_menu.add(file_menu_close)
 
134
        file_menu.add(file_menu_quit)
 
135
 
 
136
        edit_menu = gtk.Menu()
 
137
        edit_menuitem = gtk.MenuItem("_Edit")
 
138
        edit_menuitem.set_submenu(edit_menu)
 
139
 
 
140
        edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
 
141
 
 
142
        edit_menu_branchopts = gtk.MenuItem("Branch Settings")
 
143
        edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
 
144
 
 
145
        edit_menu_globopts = gtk.MenuItem("Global Settings")
 
146
        edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
 
147
 
 
148
        edit_menu.add(edit_menu_find)
 
149
        edit_menu.add(edit_menu_branchopts)
 
150
        edit_menu.add(edit_menu_globopts)
 
151
 
 
152
        view_menu = gtk.Menu()
 
153
        view_menuitem = gtk.MenuItem("_View")
 
154
        view_menuitem.set_submenu(view_menu)
 
155
 
 
156
        view_menu_refresh = self.refresh_action.create_menu_item()
 
157
        view_menu_refresh.connect('activate', self._refresh_clicked)
 
158
 
 
159
        view_menu.add(view_menu_refresh)
 
160
        view_menu.add(gtk.SeparatorMenuItem())
 
161
 
 
162
        view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
 
163
        view_menu_toolbar.set_active(True)
 
164
        view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
 
165
 
 
166
        view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
 
167
        view_menu_compact.set_active(self.compact_view)
 
168
        view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
 
169
 
 
170
        view_menu.add(view_menu_toolbar)
 
171
        view_menu.add(view_menu_compact)
 
172
        view_menu.add(gtk.SeparatorMenuItem())
 
173
 
 
174
        self.mnu_show_revno_column = gtk.CheckMenuItem("Show Revision _Number Column")
 
175
        self.mnu_show_date_column = gtk.CheckMenuItem("Show _Date Column")
 
176
 
 
177
        # Revision numbers are pointless if there are multiple branches
 
178
        if len(self.start_revs) > 1:
 
179
            self.mnu_show_revno_column.set_sensitive(False)
 
180
            self.treeview.set_property('revno-column-visible', False)
 
181
 
 
182
        for (col, name) in [(self.mnu_show_revno_column, "revno"), 
 
183
                            (self.mnu_show_date_column, "date")]:
 
184
            col.set_active(self.treeview.get_property(name + "-column-visible"))
 
185
            col.connect('toggled', self._col_visibility_changed, name)
 
186
            view_menu.add(col)
 
187
 
 
188
        go_menu = gtk.Menu()
 
189
        go_menu.set_accel_group(self.accel_group)
 
190
        go_menuitem = gtk.MenuItem("_Go")
 
191
        go_menuitem.set_submenu(go_menu)
 
192
        
 
193
        go_menu_next = self.next_rev_action.create_menu_item()
 
194
        go_menu_prev = self.prev_rev_action.create_menu_item()
 
195
 
 
196
        tag_image = gtk.Image()
 
197
        tag_image.set_from_file(icon_path("tag-16.png"))
 
198
        self.go_menu_tags = gtk.ImageMenuItem("_Tags")
 
199
        self.go_menu_tags.set_image(tag_image)
 
200
        self._update_tags()
 
201
 
 
202
        go_menu.add(go_menu_next)
 
203
        go_menu.add(go_menu_prev)
 
204
        go_menu.add(gtk.SeparatorMenuItem())
 
205
        go_menu.add(self.go_menu_tags)
 
206
 
 
207
        self.revision_menu = RevisionMenu(self.branch.repository, [], self.branch, parent=self)
 
208
        revision_menuitem = gtk.MenuItem("_Revision")
 
209
        revision_menuitem.set_submenu(self.revision_menu)
 
210
 
 
211
        branch_menu = gtk.Menu()
 
212
        branch_menuitem = gtk.MenuItem("_Branch")
 
213
        branch_menuitem.set_submenu(branch_menu)
 
214
 
 
215
        branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
 
216
        branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
 
217
 
 
218
        try:
 
219
            from bzrlib.plugins import search
 
220
        except ImportError:
 
221
            mutter("Didn't find search plugin")
 
222
        else:
 
223
            branch_menu.add(gtk.SeparatorMenuItem())
 
224
 
 
225
            branch_index_menuitem = gtk.MenuItem("_Index")
 
226
            branch_index_menuitem.connect('activate', self._branch_index_cb)
 
227
            branch_menu.add(branch_index_menuitem)
 
228
 
 
229
            branch_search_menuitem = gtk.MenuItem("_Search")
 
230
            branch_search_menuitem.connect('activate', self._branch_search_cb)
 
231
            branch_menu.add(branch_search_menuitem)
 
232
 
 
233
        help_menu = gtk.Menu()
 
234
        help_menuitem = gtk.MenuItem("_Help")
 
235
        help_menuitem.set_submenu(help_menu)
 
236
 
 
237
        help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
 
238
        help_about_menuitem.connect('activate', self._about_dialog_cb)
 
239
 
 
240
        help_menu.add(help_about_menuitem)
 
241
 
 
242
        menubar.add(file_menuitem)
 
243
        menubar.add(edit_menuitem)
 
244
        menubar.add(view_menuitem)
 
245
        menubar.add(go_menuitem)
 
246
        menubar.add(revision_menuitem)
 
247
        menubar.add(branch_menuitem)
 
248
        menubar.add(help_menuitem)
 
249
        menubar.show_all()
 
250
 
 
251
        return menubar
 
252
 
 
253
    def construct_top(self):
 
254
        """Construct the top-half of the window."""
 
255
        # FIXME: Make broken_line_length configurable
 
256
 
 
257
        self.treeview = TreeView(self.branch, self.start_revs, self.maxnum, self.compact_view)
 
258
 
 
259
        self.treeview.connect('revision-selected',
 
260
                self._treeselection_changed_cb)
 
261
        self.treeview.connect('revision-activated',
 
262
                self._tree_revision_activated)
 
263
 
 
264
        self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
 
265
 
 
266
        for col in ["revno", "date"]:
 
267
            option = self.config.get_user_option(col + '-column-visible')
 
268
            if option is not None:
 
269
                self.treeview.set_property(col + '-column-visible', option == 'True')
 
270
            else:
 
271
                self.treeview.set_property(col + '-column-visible', False)
 
272
 
 
273
        self.treeview.show()
 
274
 
 
275
        align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
 
276
        align.set_padding(5, 0, 0, 0)
 
277
        align.add(self.treeview)
 
278
        align.show()
 
279
 
 
280
        return align
 
281
 
 
282
    def construct_navigation(self):
 
283
        """Construct the navigation buttons."""
 
284
        self.toolbar = gtk.Toolbar()
 
285
        self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
 
286
 
 
287
        self.prev_button = self.prev_rev_action.create_tool_item()
 
288
        self.toolbar.insert(self.prev_button, -1)
 
289
 
 
290
        self.next_button = self.next_rev_action.create_tool_item()
 
291
        self.toolbar.insert(self.next_button, -1)
 
292
 
 
293
        self.toolbar.insert(gtk.SeparatorToolItem(), -1)
 
294
 
 
295
        refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
 
296
        refresh_button.connect('clicked', self._refresh_clicked)
 
297
        self.toolbar.insert(refresh_button, -1)
 
298
 
 
299
        self.toolbar.show_all()
 
300
 
 
301
        return self.toolbar
 
302
 
 
303
    def construct_bottom(self):
 
304
        """Construct the bottom half of the window."""
 
305
        from bzrlib.plugins.gtk.revisionview import RevisionView
 
306
        self.revisionview = RevisionView(branch=self.branch)
 
307
        (width, height) = self.get_size()
 
308
        self.revisionview.set_size_request(width, int(height / 2.5))
 
309
        self.revisionview.show()
 
310
        self.revisionview.set_show_callback(self._show_clicked_cb)
 
311
        self.revisionview.connect('notify::revision', self._go_clicked_cb)
 
312
        self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
 
313
        return self.revisionview
 
314
 
 
315
    def _tag_selected_cb(self, menuitem, revid):
 
316
        self.treeview.set_revision_id(revid)
 
317
 
 
318
    def _treeselection_changed_cb(self, selection, *args):
 
319
        """callback for when the treeview changes."""
 
320
        revision = self.treeview.get_revision()
 
321
        parents  = self.treeview.get_parents()
 
322
        children = self.treeview.get_children()
 
323
 
 
324
        self.revision_menu.set_revision_ids([revision.revision_id])
 
325
 
 
326
        if revision and revision != NULL_REVISION:
 
327
            prev_menu = gtk.Menu()
 
328
            if len(parents) > 0:
 
329
                self.prev_rev_action.set_sensitive(True)
 
330
                for parent_id in parents:
 
331
                    if parent_id and parent_id != NULL_REVISION:
 
332
                        parent = self.branch.repository.get_revision(parent_id)
 
333
                        try:
 
334
                            str = ' (' + parent.properties['branch-nick'] + ')'
 
335
                        except KeyError:
 
336
                            str = ""
 
337
 
 
338
                        item = gtk.MenuItem(parent.message.split("\n")[0] + str)
 
339
                        item.connect('activate', self._set_revision_cb, parent_id)
 
340
                        prev_menu.add(item)
 
341
                prev_menu.show_all()
 
342
            else:
 
343
                self.prev_rev_action.set_sensitive(False)
 
344
                prev_menu.hide()
 
345
 
 
346
            self.prev_button.set_menu(prev_menu)
 
347
 
 
348
            next_menu = gtk.Menu()
 
349
            if len(children) > 0:
 
350
                self.next_rev_action.set_sensitive(True)
 
351
                for child_id in children:
 
352
                    child = self.branch.repository.get_revision(child_id)
 
353
                    try:
 
354
                        str = ' (' + child.properties['branch-nick'] + ')'
 
355
                    except KeyError:
 
356
                        str = ""
 
357
 
 
358
                    item = gtk.MenuItem(child.message.split("\n")[0] + str)
 
359
                    item.connect('activate', self._set_revision_cb, child_id)
 
360
                    next_menu.add(item)
 
361
                next_menu.show_all()
 
362
            else:
 
363
                self.next_rev_action.set_sensitive(False)
 
364
                next_menu.hide()
 
365
 
 
366
            self.next_button.set_menu(next_menu)
 
367
 
 
368
            self.revisionview.set_revision(revision)
 
369
            self.revisionview.set_children(children)
 
370
    
 
371
    def _tree_revision_activated(self, widget, path, col):
 
372
        # TODO: more than one parent
 
373
        """Callback for when a treeview row gets activated."""
 
374
        revision = self.treeview.get_revision()
 
375
        parents  = self.treeview.get_parents()
 
376
 
 
377
        if len(parents) == 0:
 
378
            parent_id = None
 
379
        else:
 
380
            parent_id = parents[0]
 
381
 
 
382
        self.show_diff(revision.revision_id, parent_id)
 
383
        self.treeview.grab_focus()
 
384
        
 
385
    def _back_clicked_cb(self, *args):
 
386
        """Callback for when the back button is clicked."""
 
387
        self.treeview.back()
 
388
        
 
389
    def _fwd_clicked_cb(self, *args):
 
390
        """Callback for when the forward button is clicked."""
 
391
        self.treeview.forward()
 
392
 
 
393
    def _go_clicked_cb(self, w, p):
 
394
        """Callback for when the go button for a parent is clicked."""
 
395
        if self.revisionview.get_revision() is not None:
 
396
            self.treeview.set_revision(self.revisionview.get_revision())
 
397
 
 
398
    def _show_clicked_cb(self, revid, parentid):
 
399
        """Callback for when the show button for a parent is clicked."""
 
400
        self.show_diff(revid, parentid)
 
401
        self.treeview.grab_focus()
 
402
 
 
403
    def _set_revision_cb(self, w, revision_id):
 
404
        self.treeview.set_revision_id(revision_id)
 
405
 
 
406
    def _brokenlines_toggled_cb(self, button):
 
407
        self.compact_view = button.get_active()
 
408
 
 
409
        if self.compact_view:
 
410
            option = 'yes'
 
411
        else:
 
412
            option = 'no'
 
413
 
 
414
        self.config.set_user_option('viz-compact-view', option)
 
415
        self.treeview.set_property('compact', self.compact_view)
 
416
        self.treeview.refresh()
 
417
 
 
418
    def _branch_index_cb(self, w):
 
419
        from bzrlib.plugins.search import index as _mod_index
 
420
        _mod_index.index_url(self.branch.base)
 
421
 
 
422
    def _branch_search_cb(self, w):
 
423
        from bzrlib.plugins.gtk.search import SearchDialog
 
424
        dialog = SearchDialog(self.branch)
 
425
        
 
426
        if dialog.run() == gtk.RESPONSE_OK:
 
427
            self.set_revision(dialog.get_revision())
 
428
 
 
429
        dialog.destroy()
 
430
 
 
431
    def _about_dialog_cb(self, w):
 
432
        from bzrlib.plugins.gtk.about import AboutDialog
 
433
 
 
434
        AboutDialog().run()
 
435
 
 
436
    def _col_visibility_changed(self, col, property):
 
437
        self.config.set_user_option(property + '-column-visible', col.get_active())
 
438
        self.treeview.set_property(property + '-column-visible', col.get_active())
 
439
 
 
440
    def _toolbar_visibility_changed(self, col):
 
441
        if col.get_active():
 
442
            self.toolbar.show() 
 
443
        else:
 
444
            self.toolbar.hide()
 
445
 
 
446
    def _show_about_cb(self, w):
 
447
        dialog = AboutDialog()
 
448
        dialog.connect('response', lambda d,r: d.destroy())
 
449
        dialog.run()
 
450
 
 
451
    def _refresh_clicked(self, w):
 
452
        self.treeview.refresh()
 
453
 
 
454
    def _update_tags(self):
 
455
        menu = gtk.Menu()
 
456
 
 
457
        if self.branch.supports_tags():
 
458
            tags = self.branch.tags.get_tag_dict().items()
 
459
            tags.sort()
 
460
            tags.reverse()
 
461
            for tag, revid in tags:
 
462
                tag_image = gtk.Image()
 
463
                tag_image.set_from_file(icon_path('tag-16.png'))
 
464
                tag_item = gtk.ImageMenuItem(tag.replace('_', '__'))
 
465
                tag_item.set_image(tag_image)
 
466
                tag_item.connect('activate', self._tag_selected_cb, revid)
 
467
                menu.add(tag_item)
 
468
            self.go_menu_tags.set_submenu(menu)
 
469
 
 
470
            self.go_menu_tags.set_sensitive(len(tags) != 0)
 
471
        else:
 
472
            self.go_menu_tags.set_sensitive(False)
 
473
 
 
474
        self.go_menu_tags.show_all()
 
475
 
 
476
    def show_diff(self, revid=None, parentid=None):
 
477
        """Open a new window to show a diff between the given revisions."""
 
478
        from bzrlib.plugins.gtk.diff import DiffWindow
 
479
        window = DiffWindow(parent=self)
 
480
 
 
481
        if parentid is None:
 
482
            parentid = NULL_REVISION
 
483
 
 
484
        rev_tree    = self.branch.repository.revision_tree(revid)
 
485
        parent_tree = self.branch.repository.revision_tree(parentid)
 
486
 
 
487
        description = revid + " - " + self.branch.nick
 
488
        window.set_diff(description, rev_tree, parent_tree)
 
489
        window.show()
 
490
 
 
491