/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: Daniel Schierbeck
  • Date: 2007-11-22 23:15:32 UTC
  • mfrom: (330.6.4 gtk)
  • Revision ID: daniel.schierbeck@gmail.com-20071122231532-9zwibd4mvq75ouc4
Merged with mainline.

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