/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: 2007-12-21 20:20:37 UTC
  • mto: This revision was merged to the branch mainline in revision 422.
  • Revision ID: jelmer@samba.org-20071221202037-6zvbwefvrte3e2zy
Fix URL.

Show diffs side-by-side

added added

removed removed

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