/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: Gary van der Merwe
  • Date: 2008-02-13 08:53:14 UTC
  • mto: (423.1.14 trunk)
  • mto: This revision was merged to the branch mainline in revision 429.
  • Revision ID: garyvdm@garyvdm-desktop-20080213085314-1hqguvfy113qb70b
Add branchview dir to setup.py

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.window import Window
 
17
from bzrlib.plugins.gtk.tags import AddTagDialog
 
18
from bzrlib.plugins.gtk.preferences import PreferencesWindow
 
19
from bzrlib.plugins.gtk.branchview import TreeView
 
20
from bzrlib.revision import Revision
 
21
from bzrlib.config import BranchConfig
 
22
from bzrlib.config import GlobalConfig
 
23
 
 
24
class BranchWindow(Window):
 
25
    """Branch window.
 
26
 
 
27
    This object represents and manages a single window containing information
 
28
    for a particular branch.
 
29
    """
 
30
 
 
31
    def __init__(self, branch, start, maxnum, parent=None):
 
32
        """Create a new BranchWindow.
 
33
 
 
34
        :param branch: Branch object for branch to show.
 
35
        :param start: Revision id of top revision.
 
36
        :param maxnum: Maximum number of revisions to display, 
 
37
                       None for no limit.
 
38
        """
 
39
 
 
40
        Window.__init__(self, parent=parent)
 
41
        self.set_border_width(0)
 
42
 
 
43
        self.branch      = branch
 
44
        self.start       = start
 
45
        self.maxnum      = maxnum
 
46
        self.config      = GlobalConfig()
 
47
 
 
48
        if self.config.get_user_option('viz-compact-view') == 'yes':
 
49
            self.compact_view = True
 
50
        else:
 
51
            self.compact_view = False
 
52
 
 
53
        self.set_title(branch.nick + " - revision history")
 
54
 
 
55
        # Use three-quarters of the screen by default
 
56
        screen = self.get_screen()
 
57
        monitor = screen.get_monitor_geometry(0)
 
58
        width = int(monitor.width * 0.75)
 
59
        height = int(monitor.height * 0.75)
 
60
        self.set_default_size(width, height)
 
61
 
 
62
        # FIXME AndyFitz!
 
63
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
 
64
        self.set_icon(icon)
 
65
 
 
66
        gtk.accel_map_add_entry("<viz>/Go/Next Revision", gtk.keysyms.Up, gtk.gdk.MOD1_MASK)
 
67
        gtk.accel_map_add_entry("<viz>/Go/Previous Revision", gtk.keysyms.Down, gtk.gdk.MOD1_MASK)
 
68
 
 
69
        self.accel_group = gtk.AccelGroup()
 
70
        self.add_accel_group(self.accel_group)
 
71
 
 
72
        gtk.Action.set_tool_item_type(gtk.MenuToolButton)
 
73
 
 
74
        self.prev_rev_action = gtk.Action("prev-rev", "_Previous Revision", "Go to the previous revision", gtk.STOCK_GO_DOWN)
 
75
        self.prev_rev_action.set_accel_path("<viz>/Go/Previous Revision")
 
76
        self.prev_rev_action.set_accel_group(self.accel_group)
 
77
        self.prev_rev_action.connect("activate", self._back_clicked_cb)
 
78
        self.prev_rev_action.connect_accelerator()
 
79
 
 
80
        self.next_rev_action = gtk.Action("next-rev", "_Next Revision", "Go to the next revision", gtk.STOCK_GO_UP)
 
81
        self.next_rev_action.set_accel_path("<viz>/Go/Next Revision")
 
82
        self.next_rev_action.set_accel_group(self.accel_group)
 
83
        self.next_rev_action.connect("activate", self._fwd_clicked_cb)
 
84
        self.next_rev_action.connect_accelerator()
 
85
 
 
86
        self.construct()
 
87
 
 
88
    def set_revision(self, revid):
 
89
        self.treeview.set_revision_id(revid)
 
90
 
 
91
    def construct(self):
 
92
        """Construct the window contents."""
 
93
        vbox = gtk.VBox(spacing=0)
 
94
        self.add(vbox)
 
95
 
 
96
        self.paned = gtk.VPaned()
 
97
        self.paned.pack1(self.construct_top(), resize=True, shrink=False)
 
98
        self.paned.pack2(self.construct_bottom(), resize=False, shrink=True)
 
99
        self.paned.show()
 
100
 
 
101
        vbox.pack_start(self.construct_menubar(), expand=False, fill=True)
 
102
        vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
 
103
        vbox.pack_start(self.construct_loading_msg(), expand=False, fill=True)
 
104
        
 
105
        vbox.pack_start(self.paned, expand=True, fill=True)
 
106
        vbox.set_focus_child(self.paned)
 
107
 
 
108
        vbox.show()
 
109
 
 
110
    def construct_menubar(self):
 
111
        menubar = gtk.MenuBar()
 
112
 
 
113
        file_menu = gtk.Menu()
 
114
        file_menuitem = gtk.MenuItem("_File")
 
115
        file_menuitem.set_submenu(file_menu)
 
116
 
 
117
        file_menu_close = gtk.ImageMenuItem(gtk.STOCK_CLOSE, self.accel_group)
 
118
        file_menu_close.connect('activate', lambda x: self.destroy())
 
119
        
 
120
        file_menu_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accel_group)
 
121
        file_menu_quit.connect('activate', lambda x: gtk.main_quit())
 
122
        
 
123
        if self._parent is not None:
 
124
            file_menu.add(file_menu_close)
 
125
        file_menu.add(file_menu_quit)
 
126
 
 
127
        edit_menu = gtk.Menu()
 
128
        edit_menuitem = gtk.MenuItem("_Edit")
 
129
        edit_menuitem.set_submenu(edit_menu)
 
130
 
 
131
        edit_menu_find = gtk.ImageMenuItem(gtk.STOCK_FIND)
 
132
 
 
133
        edit_menu_branchopts = gtk.MenuItem("Branch Settings")
 
134
        edit_menu_branchopts.connect('activate', lambda x: PreferencesWindow(self.branch.get_config()).show())
 
135
 
 
136
        edit_menu_globopts = gtk.MenuItem("Global Settings")
 
137
        edit_menu_globopts.connect('activate', lambda x: PreferencesWindow().show())
 
138
 
 
139
        edit_menu.add(edit_menu_find)
 
140
        edit_menu.add(edit_menu_branchopts)
 
141
        edit_menu.add(edit_menu_globopts)
 
142
 
 
143
        view_menu = gtk.Menu()
 
144
        view_menuitem = gtk.MenuItem("_View")
 
145
        view_menuitem.set_submenu(view_menu)
 
146
 
 
147
        view_menu_toolbar = gtk.CheckMenuItem("Show Toolbar")
 
148
        view_menu_toolbar.set_active(True)
 
149
        view_menu_toolbar.connect('toggled', self._toolbar_visibility_changed)
 
150
 
 
151
        view_menu_compact = gtk.CheckMenuItem("Show Compact Graph")
 
152
        view_menu_compact.set_active(self.compact_view)
 
153
        view_menu_compact.connect('activate', self._brokenlines_toggled_cb)
 
154
 
 
155
        view_menu.add(view_menu_toolbar)
 
156
        view_menu.add(view_menu_compact)
 
157
        view_menu.add(gtk.SeparatorMenuItem())
 
158
 
 
159
        for (label, name) in [("Revision _Number", "revno"), ("_Date", "date")]:
 
160
            col = gtk.CheckMenuItem("Show " + label + " Column")
 
161
            col.set_active(self.treeview.get_property(name + "-column-visible"))
 
162
            col.connect('toggled', self._col_visibility_changed, name)
 
163
            view_menu.add(col)
 
164
 
 
165
        go_menu = gtk.Menu()
 
166
        go_menu.set_accel_group(self.accel_group)
 
167
        go_menuitem = gtk.MenuItem("_Go")
 
168
        go_menuitem.set_submenu(go_menu)
 
169
        
 
170
        go_menu_next = self.next_rev_action.create_menu_item()
 
171
        go_menu_prev = self.prev_rev_action.create_menu_item()
 
172
 
 
173
        self.go_menu_tags = gtk.MenuItem("_Tags")
 
174
        self._update_tags()
 
175
 
 
176
        go_menu.add(go_menu_next)
 
177
        go_menu.add(go_menu_prev)
 
178
        go_menu.add(gtk.SeparatorMenuItem())
 
179
        go_menu.add(self.go_menu_tags)
 
180
 
 
181
        revision_menu = gtk.Menu()
 
182
        revision_menuitem = gtk.MenuItem("_Revision")
 
183
        revision_menuitem.set_submenu(revision_menu)
 
184
 
 
185
        revision_menu_diff = gtk.MenuItem("View Changes")
 
186
        revision_menu_diff.connect('activate', 
 
187
                lambda w: self.treeview.show_diff())
 
188
 
 
189
        revision_menu_tag = gtk.MenuItem("Tag Revision")
 
190
        revision_menu_tag.connect('activate', self._tag_revision_cb)
 
191
 
 
192
        revision_menu.add(revision_menu_tag)
 
193
        revision_menu.add(revision_menu_diff)
 
194
 
 
195
        branch_menu = gtk.Menu()
 
196
        branch_menuitem = gtk.MenuItem("_Branch")
 
197
        branch_menuitem.set_submenu(branch_menu)
 
198
 
 
199
        branch_menu.add(gtk.MenuItem("Pu_ll Revisions"))
 
200
        branch_menu.add(gtk.MenuItem("Pu_sh Revisions"))
 
201
 
 
202
        help_menu = gtk.Menu()
 
203
        help_menuitem = gtk.MenuItem("_Help")
 
204
        help_menuitem.set_submenu(help_menu)
 
205
 
 
206
        help_about_menuitem = gtk.ImageMenuItem(gtk.STOCK_ABOUT, self.accel_group)
 
207
        help_about_menuitem.connect('activate', self._about_dialog_cb)
 
208
 
 
209
        help_menu.add(help_about_menuitem)
 
210
 
 
211
        menubar.add(file_menuitem)
 
212
        menubar.add(edit_menuitem)
 
213
        menubar.add(view_menuitem)
 
214
        menubar.add(go_menuitem)
 
215
        menubar.add(revision_menuitem)
 
216
        menubar.add(branch_menuitem)
 
217
        menubar.add(help_menuitem)
 
218
        menubar.show_all()
 
219
 
 
220
        return menubar
 
221
    
 
222
    def construct_loading_msg(self):
 
223
        image_loading = gtk.image_new_from_stock(gtk.STOCK_REFRESH,
 
224
                                                 gtk.ICON_SIZE_BUTTON)
 
225
        image_loading.show()
 
226
        
 
227
        label_loading = gtk.Label(_("Please wait, loading ancestral graph..."))
 
228
        label_loading.set_alignment(0.0, 0.5)
 
229
        label_loading.show()
 
230
        
 
231
        self.loading_msg_box = gtk.HBox()
 
232
        self.loading_msg_box.set_spacing(5)
 
233
        self.loading_msg_box.set_border_width(5)        
 
234
        self.loading_msg_box.pack_start(image_loading, False, False)
 
235
        self.loading_msg_box.pack_start(label_loading, True, True)
 
236
        self.loading_msg_box.show()
 
237
        
 
238
        return self.loading_msg_box
 
239
 
 
240
    def construct_top(self):
 
241
        """Construct the top-half of the window."""
 
242
        # FIXME: Make broken_line_length configurable
 
243
 
 
244
        self.treeview = TreeView(self.branch, self.start, self.maxnum, self.compact_view)
 
245
 
 
246
        self.treeview.connect('revision-selected',
 
247
                self._treeselection_changed_cb)
 
248
 
 
249
        self.treeview.connect('revisions-loaded', 
 
250
                lambda x: self.loading_msg_box.hide())
 
251
 
 
252
        self.treeview.connect('tag-added', lambda w, t, r: self._update_tags())
 
253
 
 
254
        for col in ["revno", "date"]:
 
255
            option = self.config.get_user_option(col + '-column-visible')
 
256
            if option is not None:
 
257
                self.treeview.set_property(col + '-column-visible', option == 'True')
 
258
 
 
259
        self.treeview.show()
 
260
 
 
261
        align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
 
262
        align.set_padding(5, 0, 0, 0)
 
263
        align.add(self.treeview)
 
264
        align.show()
 
265
 
 
266
        return align
 
267
 
 
268
    def construct_navigation(self):
 
269
        """Construct the navigation buttons."""
 
270
        self.toolbar = gtk.Toolbar()
 
271
        self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
 
272
 
 
273
        self.prev_button = self.prev_rev_action.create_tool_item()
 
274
        self.toolbar.insert(self.prev_button, -1)
 
275
 
 
276
        self.next_button = self.next_rev_action.create_tool_item()
 
277
        self.toolbar.insert(self.next_button, -1)
 
278
 
 
279
        self.toolbar.insert(gtk.SeparatorToolItem(), -1)
 
280
 
 
281
        refresh_button = gtk.ToolButton(gtk.STOCK_REFRESH)
 
282
        refresh_button.connect('clicked', self._refresh_clicked)
 
283
        self.toolbar.insert(refresh_button, -1)
 
284
 
 
285
        self.toolbar.show_all()
 
286
 
 
287
        return self.toolbar
 
288
 
 
289
    def construct_bottom(self):
 
290
        """Construct the bottom half of the window."""
 
291
        from bzrlib.plugins.gtk.revisionview import RevisionView
 
292
        self.revisionview = RevisionView(branch=self.branch)
 
293
        (width, height) = self.get_size()
 
294
        self.revisionview.set_size_request(width, int(height / 2.5))
 
295
        self.revisionview.show()
 
296
        self.revisionview.set_show_callback(self._show_clicked_cb)
 
297
        self.revisionview.connect('notify::revision', self._go_clicked_cb)
 
298
        self.treeview.connect('tag-added', lambda w, t, r: self.revisionview.update_tags())
 
299
        return self.revisionview
 
300
 
 
301
    def _tag_selected_cb(self, menuitem, revid):
 
302
        self.treeview.set_revision_id(revid)
 
303
 
 
304
    def _treeselection_changed_cb(self, selection, *args):
 
305
        """callback for when the treeview changes."""
 
306
        revision = self.treeview.get_revision()
 
307
        parents  = self.treeview.get_parents()
 
308
        children = self.treeview.get_children()
 
309
 
 
310
        if revision is not None:
 
311
            prev_menu = gtk.Menu()
 
312
            if len(parents) > 0:
 
313
                self.prev_rev_action.set_sensitive(True)
 
314
                for parent_id in parents:
 
315
                    parent = self.branch.repository.get_revision(parent_id)
 
316
                    try:
 
317
                        str = ' (' + parent.properties['branch-nick'] + ')'
 
318
                    except KeyError:
 
319
                        str = ""
 
320
 
 
321
                    item = gtk.MenuItem(parent.message.split("\n")[0] + str)
 
322
                    item.connect('activate', self._set_revision_cb, parent_id)
 
323
                    prev_menu.add(item)
 
324
                prev_menu.show_all()
 
325
            else:
 
326
                self.prev_rev_action.set_sensitive(False)
 
327
                prev_menu.hide()
 
328
 
 
329
            self.prev_button.set_menu(prev_menu)
 
330
 
 
331
            next_menu = gtk.Menu()
 
332
            if len(children) > 0:
 
333
                self.next_rev_action.set_sensitive(True)
 
334
                for child_id in children:
 
335
                    child = self.branch.repository.get_revision(child_id)
 
336
                    try:
 
337
                        str = ' (' + child.properties['branch-nick'] + ')'
 
338
                    except KeyError:
 
339
                        str = ""
 
340
 
 
341
                    item = gtk.MenuItem(child.message.split("\n")[0] + str)
 
342
                    item.connect('activate', self._set_revision_cb, child_id)
 
343
                    next_menu.add(item)
 
344
                next_menu.show_all()
 
345
            else:
 
346
                self.next_rev_action.set_sensitive(False)
 
347
                next_menu.hide()
 
348
 
 
349
            self.next_button.set_menu(next_menu)
 
350
 
 
351
            self.revisionview.set_revision(revision)
 
352
            self.revisionview.set_children(children)
 
353
 
 
354
    def _back_clicked_cb(self, *args):
 
355
        """Callback for when the back button is clicked."""
 
356
        self.treeview.back()
 
357
        
 
358
    def _fwd_clicked_cb(self, *args):
 
359
        """Callback for when the forward button is clicked."""
 
360
        self.treeview.forward()
 
361
 
 
362
    def _go_clicked_cb(self, w, p):
 
363
        """Callback for when the go button for a parent is clicked."""
 
364
        if self.revisionview.get_revision() is not None:
 
365
            self.treeview.set_revision(self.revisionview.get_revision())
 
366
 
 
367
    def _show_clicked_cb(self, revid, parentid):
 
368
        """Callback for when the show button for a parent is clicked."""
 
369
        self.treeview.show_diff(revid, parentid)
 
370
        self.treeview.grab_focus()
 
371
 
 
372
    def _set_revision_cb(self, w, revision_id):
 
373
        self.treeview.set_revision_id(revision_id)
 
374
 
 
375
    def _brokenlines_toggled_cb(self, button):
 
376
        self.compact_view = button.get_active()
 
377
 
 
378
        if self.compact_view:
 
379
            option = 'yes'
 
380
        else:
 
381
            option = 'no'
 
382
 
 
383
        self.config.set_user_option('viz-compact-view', option)
 
384
        self.treeview.set_property('compact', self.compact_view)
 
385
        self.treeview.refresh()
 
386
 
 
387
    def _tag_revision_cb(self, w):
 
388
        try:
 
389
            self.treeview.set_sensitive(False)
 
390
            dialog = AddTagDialog(self.branch.repository, self.treeview.get_revision().revision_id, self.branch)
 
391
            response = dialog.run()
 
392
            if response != gtk.RESPONSE_NONE:
 
393
                dialog.hide()
 
394
            
 
395
                if response == gtk.RESPONSE_OK:
 
396
                    self.treeview.add_tag(dialog.tagname, dialog._revid)
 
397
                
 
398
                dialog.destroy()
 
399
 
 
400
        finally:
 
401
            self.treeview.set_sensitive(True)
 
402
 
 
403
    def _about_dialog_cb(self, w):
 
404
        from bzrlib.plugins.gtk.about import AboutDialog
 
405
 
 
406
        AboutDialog().run()
 
407
 
 
408
    def _col_visibility_changed(self, col, property):
 
409
        self.config.set_user_option(property + '-column-visible', col.get_active())
 
410
        self.treeview.set_property(property + '-column-visible', col.get_active())
 
411
 
 
412
    def _toolbar_visibility_changed(self, col):
 
413
        if col.get_active():
 
414
            self.toolbar.show() 
 
415
        else:
 
416
            self.toolbar.hide()
 
417
 
 
418
    def _show_about_cb(self, w):
 
419
        dialog = AboutDialog()
 
420
        dialog.connect('response', lambda d,r: d.destroy())
 
421
        dialog.run()
 
422
 
 
423
    def _refresh_clicked(self, w):
 
424
        self.treeview.update()
 
425
 
 
426
    def _update_tags(self):
 
427
        menu = gtk.Menu()
 
428
 
 
429
        if self.branch.supports_tags():
 
430
            tags = self.branch.tags.get_tag_dict().items()
 
431
            tags.sort()
 
432
            tags.reverse()
 
433
            for tag, revid in tags:
 
434
                tag_item = gtk.MenuItem(tag)
 
435
                tag_item.connect('activate', self._tag_selected_cb, revid)
 
436
                menu.add(tag_item)
 
437
            self.go_menu_tags.set_submenu(menu)
 
438
 
 
439
            self.go_menu_tags.set_sensitive(len(tags) != 0)
 
440
        else:
 
441
            self.go_menu_tags.set_sensitive(False)
 
442
 
 
443
        self.go_menu_tags.show_all()
 
444
 
 
445