/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
1 by Scott James Remnant
Commit the first version of bzrk.
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
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
15
import treemodel
1 by Scott James Remnant
Commit the first version of bzrk.
16
17
from bzrlib.osutils import format_date
18
256.2.5 by Gary van der Merwe
Oops - Renamed file without changeing import lines
19
from linegraph import linegraph, same_branch
1 by Scott James Remnant
Commit the first version of bzrk.
20
from graphcell import CellRendererGraph
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
21
from treemodel import TreeModel
1 by Scott James Remnant
Commit the first version of bzrk.
22
23
24
class BranchWindow(gtk.Window):
25
    """Branch window.
26
27
    This object represents and manages a single window containing information
28
    for a particular branch.
29
    """
30
275.1.6 by Daniel Schierbeck
Made Ctrl-W call gtk.main_quit if the window has no parent.
31
    def __init__(self, parent=None):
1 by Scott James Remnant
Commit the first version of bzrk.
32
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
33
        self.set_border_width(0)
34
        self.set_title("bzrk")
35
275.1.6 by Daniel Schierbeck
Made Ctrl-W call gtk.main_quit if the window has no parent.
36
        self._parent = parent
37
38
        self.connect('key-press-event', self._on_key_pressed)
275.1.1 by Daniel Schierbeck
Close branch visualization window when 'destroy' message is received.
39
1 by Scott James Remnant
Commit the first version of bzrk.
40
        # Use three-quarters of the screen by default
41
        screen = self.get_screen()
4 by Scott James Remnant
Use the size of the first monitor rather than the entire screen
42
        monitor = screen.get_monitor_geometry(0)
43
        width = int(monitor.width * 0.75)
44
        height = int(monitor.height * 0.75)
1 by Scott James Remnant
Commit the first version of bzrk.
45
        self.set_default_size(width, height)
46
47
        # FIXME AndyFitz!
48
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
49
        self.set_icon(icon)
50
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
51
        self.accel_group = gtk.AccelGroup()
52
        self.add_accel_group(self.accel_group)
53
1 by Scott James Remnant
Commit the first version of bzrk.
54
        self.construct()
55
56
    def construct(self):
57
        """Construct the window contents."""
44 by David Allouche
reorganise branch window
58
        vbox = gtk.VBox(spacing=0)
59
        self.add(vbox)
60
61
        vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
62
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
63
        paned = gtk.VPaned()
64
        paned.pack1(self.construct_top(), resize=True, shrink=False)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
65
        paned.pack2(self.construct_bottom(), resize=False, shrink=True)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
66
        paned.show()
44 by David Allouche
reorganise branch window
67
        vbox.pack_start(paned, expand=True, fill=True)
68
        vbox.set_focus_child(paned)
69
70
        vbox.show()
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
71
72
    def construct_top(self):
73
        """Construct the top-half of the window."""
1 by Scott James Remnant
Commit the first version of bzrk.
74
        scrollwin = gtk.ScrolledWindow()
75
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
76
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
77
        scrollwin.show()
78
79
        self.treeview = gtk.TreeView()
80
        self.treeview.set_rules_hint(True)
81
        self.treeview.set_search_column(4)
280.1.2 by Daniel Schierbeck
Switched to handle the 'changed' signal from the treeview's treeselection instead of the 'cursor-changed' signal from the treeview itself, allowing more flexibility (particularly the ability to handle programmatic selections)
82
        self.treeview.get_selection().connect("changed", self._treeselection_changed_cb)
47 by Wouter van Heyst
rename _treeview_clicked_cb to correcter _treeview_row_activated_cb
83
        self.treeview.connect("row-activated", self._treeview_row_activated_cb)
226 by Jelmer Vernooij
Add context menu in bzrk.
84
        self.treeview.connect("button-release-event", 
85
                self._treeview_row_mouseclick)
256.4.4 by Gary van der Merwe
Use fixed-height-mode on treeview so that revisions data is only
86
        self.treeview.set_property('fixed-height-mode', True)
1 by Scott James Remnant
Commit the first version of bzrk.
87
        scrollwin.add(self.treeview)
88
        self.treeview.show()
89
256.2.27 by Gary van der Merwe
Show Revision No in tree
90
        cell = gtk.CellRendererText()
256.4.4 by Gary van der Merwe
Use fixed-height-mode on treeview so that revisions data is only
91
        cell.set_property("width-chars", 15)
256.4.1 by Gary van der Merwe
* Set a width and use ellips on Revision No column.
92
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
256.2.27 by Gary van der Merwe
Show Revision No in tree
93
        column = gtk.TreeViewColumn("Revision No")
94
        column.set_resizable(True)
256.4.4 by Gary van der Merwe
Use fixed-height-mode on treeview so that revisions data is only
95
        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
96
        column.set_fixed_width(cell.get_size(self.treeview)[2])
256.2.27 by Gary van der Merwe
Show Revision No in tree
97
        column.pack_start(cell, expand=True)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
98
        column.add_attribute(cell, "text", treemodel.REVNO)
256.2.27 by Gary van der Merwe
Show Revision No in tree
99
        self.treeview.append_column(column)
100
256.4.1 by Gary van der Merwe
* Set a width and use ellips on Revision No column.
101
        self.graph_cell = CellRendererGraph()
256.4.4 by Gary van der Merwe
Use fixed-height-mode on treeview so that revisions data is only
102
        self.graph_column = gtk.TreeViewColumn()
103
        self.graph_column.set_resizable(True)
104
        self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
105
        self.graph_column.pack_start(self.graph_cell, expand=False)
106
        self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
107
        self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
108
        self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
109
        self.treeview.append_column(self.graph_column)
1 by Scott James Remnant
Commit the first version of bzrk.
110
111
        cell = gtk.CellRendererText()
112
        cell.set_property("width-chars", 40)
113
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
114
        column = gtk.TreeViewColumn("Message")
115
        column.set_resizable(True)
256.4.4 by Gary van der Merwe
Use fixed-height-mode on treeview so that revisions data is only
116
        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
117
        column.set_fixed_width(cell.get_size(self.treeview)[2])
1 by Scott James Remnant
Commit the first version of bzrk.
118
        column.pack_start(cell, expand=True)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
119
        column.add_attribute(cell, "text", treemodel.MESSAGE)
1 by Scott James Remnant
Commit the first version of bzrk.
120
        self.treeview.append_column(column)
121
122
        cell = gtk.CellRendererText()
123
        cell.set_property("width-chars", 40)
124
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
125
        column = gtk.TreeViewColumn("Committer")
126
        column.set_resizable(True)
256.4.4 by Gary van der Merwe
Use fixed-height-mode on treeview so that revisions data is only
127
        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
128
        column.set_fixed_width(cell.get_size(self.treeview)[2])
1 by Scott James Remnant
Commit the first version of bzrk.
129
        column.pack_start(cell, expand=True)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
130
        column.add_attribute(cell, "text", treemodel.COMMITER)
1 by Scott James Remnant
Commit the first version of bzrk.
131
        self.treeview.append_column(column)
132
133
        cell = gtk.CellRendererText()
256.4.4 by Gary van der Merwe
Use fixed-height-mode on treeview so that revisions data is only
134
        cell.set_property("width-chars", 40)
1 by Scott James Remnant
Commit the first version of bzrk.
135
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
136
        column = gtk.TreeViewColumn("Date")
137
        column.set_resizable(True)
256.4.4 by Gary van der Merwe
Use fixed-height-mode on treeview so that revisions data is only
138
        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
139
        column.set_fixed_width(cell.get_size(self.treeview)[2])
1 by Scott James Remnant
Commit the first version of bzrk.
140
        column.pack_start(cell, expand=True)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
141
        column.add_attribute(cell, "text", treemodel.TIMESTAMP)
1 by Scott James Remnant
Commit the first version of bzrk.
142
        self.treeview.append_column(column)
143
44 by David Allouche
reorganise branch window
144
        return scrollwin
145
146
    def construct_navigation(self):
147
        """Construct the navigation buttons."""
148
        frame = gtk.Frame()
149
        frame.set_shadow_type(gtk.SHADOW_OUT)
150
        frame.show()
151
        
152
        hbox = gtk.HBox(spacing=12)
153
        frame.add(hbox)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
154
        hbox.show()
155
156
        self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
44 by David Allouche
reorganise branch window
157
        self.back_button.set_relief(gtk.RELIEF_NONE)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
158
        self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
159
                                         0, 0)
160
        self.back_button.connect("clicked", self._back_clicked_cb)
161
        hbox.pack_start(self.back_button, expand=False, fill=True)
162
        self.back_button.show()
163
164
        self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
44 by David Allouche
reorganise branch window
165
        self.fwd_button.set_relief(gtk.RELIEF_NONE)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
166
        self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
167
                                        0, 0)
168
        self.fwd_button.connect("clicked", self._fwd_clicked_cb)
169
        hbox.pack_start(self.fwd_button, expand=False, fill=True)
170
        self.fwd_button.show()
171
44 by David Allouche
reorganise branch window
172
        return frame
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
173
174
    def construct_bottom(self):
175
        """Construct the bottom half of the window."""
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
176
        from bzrlib.plugins.gtk.logview import LogView
256.2.23 by Gary van der Merwe
Show Children
177
        self.logview = LogView(None, True, [], True)
44 by David Allouche
reorganise branch window
178
        (width, height) = self.get_size()
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
179
        self.logview.set_size_request(width, int(height / 2.5))
180
        self.logview.show()
181
        self.logview.set_show_callback(self._show_clicked_cb)
182
        self.logview.set_go_callback(self._go_clicked_cb)
183
        return self.logview
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
184
40 by David Allouche
remove --robust, pyflakes fixes, update README
185
    def set_branch(self, branch, start, maxnum):
1 by Scott James Remnant
Commit the first version of bzrk.
186
        """Set the branch and start position for this window.
187
188
        Creates a new TreeModel and populates it with information about
189
        the new branch before updating the window title and model of the
190
        treeview itself.
191
        """
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
192
        self.branch = branch
265.1.1 by Gary van der Merwe
Show bzr viz interface quickly.
193
        self.set_title(branch.nick + " - bzrk")
194
        gobject.idle_add(self.populate_model, start, maxnum)
195
196
    def populate_model(self, start, maxnum):
256.4.1 by Gary van der Merwe
* Set a width and use ellips on Revision No column.
197
        (linegraphdata, index, columns_len) = linegraph(self.branch,
198
                                                        start,
199
                                                        maxnum)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
200
        self.model = TreeModel(self.branch, linegraphdata)
256.4.1 by Gary van der Merwe
* Set a width and use ellips on Revision No column.
201
        self.graph_cell.columns_len = columns_len
256.4.4 by Gary van der Merwe
Use fixed-height-mode on treeview so that revisions data is only
202
        width = self.graph_cell.get_size(self.treeview)[2]
203
        self.graph_column.set_fixed_width(width)
204
        self.graph_column.set_max_width(width)
256.2.21 by Gary van der Merwe
Refactor so that line graph is more contained.
205
        self.index = index
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
206
        self.treeview.set_model(self.model)
280.1.1 by Daniel Schierbeck
Select the tip revision when opening the visualization window
207
        self.treeview.get_selection().select_path(0)
265.1.1 by Gary van der Merwe
Show bzr viz interface quickly.
208
        return False
209
    
275.1.5 by Daniel Schierbeck
Made windows close correctly on Ctrl-W and made the application quit on Ctrl-Q.
210
    def _on_key_pressed(self, widget, event):
211
        """ Key press event handler. """
212
        keyname = gtk.gdk.keyval_name(event.keyval)
213
        func = getattr(self, '_on_key_press_' + keyname, None)
214
        if func:
215
            return func(event)
216
217
    def _on_key_press_w(self, event):
218
        if event.state & gtk.gdk.CONTROL_MASK:
219
            self.destroy()
275.1.6 by Daniel Schierbeck
Made Ctrl-W call gtk.main_quit if the window has no parent.
220
            if self._parent is None:
221
                gtk.main_quit()
275.1.5 by Daniel Schierbeck
Made windows close correctly on Ctrl-W and made the application quit on Ctrl-Q.
222
223
    def _on_key_press_q(self, event):
224
        if event.state & gtk.gdk.CONTROL_MASK:
225
            gtk.main_quit()
265.1.1 by Gary van der Merwe
Show bzr viz interface quickly.
226
    
280.1.2 by Daniel Schierbeck
Switched to handle the 'changed' signal from the treeview's treeselection instead of the 'cursor-changed' signal from the treeview itself, allowing more flexibility (particularly the ability to handle programmatic selections)
227
    def _treeselection_changed_cb(self, selection, *args):
228
        """Callback for when the treeview changes."""
229
        (model, selected_rows) = selection.get_selected_rows()
230
        if len(selected_rows) > 0:
256.2.62 by Gary van der Merwe
Merge Trunk.
231
            iter = self.model.get_iter(selected_rows[0])
232
            revision = self.model.get_value(iter, treemodel.REVISION)
233
            parents = self.model.get_value(iter, treemodel.PARENTS)
234
            children = self.model.get_value(iter, treemodel.CHILDREN)
235
            
236
            self.back_button.set_sensitive(len(parents) > 0)
237
            self.fwd_button.set_sensitive(len(children) > 0)
280.1.2 by Daniel Schierbeck
Switched to handle the 'changed' signal from the treeview's treeselection instead of the 'cursor-changed' signal from the treeview itself, allowing more flexibility (particularly the ability to handle programmatic selections)
238
            tags = []
239
            if self.branch.supports_tags():
240
                tagdict = self.branch.tags.get_reverse_tag_dict()
241
                if tagdict.has_key(revision.revision_id):
242
                    tags = tagdict[revision.revision_id]
256.2.62 by Gary van der Merwe
Merge Trunk.
243
            self.logview.set_revision(revision, tags, children)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
244
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
245
    def _back_clicked_cb(self, *args):
246
        """Callback for when the back button is clicked."""
247
        (path, col) = self.treeview.get_cursor()
248
        revision = self.model[path][0]
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
249
        parents = self.model[path][4]
250
        if not len(parents):
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
251
            return
252
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
253
        for parent_id in parents:
254
            parent = self.revisions[self.index[parent_id]]
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
255
            if same_branch(revision, parent):
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
256
                self.treeview.set_cursor(self.index[parent_id])
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
257
                break
258
        else:
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
259
            self.treeview.set_cursor(self.index[parents[0]])
13 by Scott James Remnant
Keep the focus on the treeview
260
        self.treeview.grab_focus()
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
261
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
262
    def _fwd_clicked_cb(self, *args):
263
        """Callback for when the forward button is clicked."""
264
        (path, col) = self.treeview.get_cursor()
265
        revision = self.model[path][0]
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
266
        children = self.model[path][5]
267
        if not len(children):
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
268
            return
269
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
270
        for child_id in children:
271
            child = self.revisions[self.index[child_id]]
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
272
            if same_branch(child, revision):
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
273
                self.treeview.set_cursor(self.index[child_id])
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
274
                break
275
        else:
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
276
            self.treeview.set_cursor(self.index[children[0]])
13 by Scott James Remnant
Keep the focus on the treeview
277
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
278
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
279
    def _go_clicked_cb(self, revid):
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
280
        """Callback for when the go button for a parent is clicked."""
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
281
        self.treeview.set_cursor(self.index[revid])
13 by Scott James Remnant
Keep the focus on the treeview
282
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
283
152 by Jelmer Vernooij
Cleanup some more code.
284
    def show_diff(self, branch, revid, parentid):
285
        """Open a new window to show a diff between the given revisions."""
286
        from bzrlib.plugins.gtk.diff import DiffWindow
287
        window = DiffWindow()
226 by Jelmer Vernooij
Add context menu in bzrk.
288
        (parent_tree, rev_tree) = branch.repository.revision_trees([parentid, 
289
                                                                   revid])
152 by Jelmer Vernooij
Cleanup some more code.
290
        description = revid + " - " + branch.nick
291
        window.set_diff(description, rev_tree, parent_tree)
292
        window.show()
293
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
294
    def _show_clicked_cb(self, revid, parentid):
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
295
        """Callback for when the show button for a parent is clicked."""
152 by Jelmer Vernooij
Cleanup some more code.
296
        self.show_diff(self.branch, revid, parentid)
13 by Scott James Remnant
Keep the focus on the treeview
297
        self.treeview.grab_focus()
46 by Wouter van Heyst
show diff on row activation, LP# 44591
298
226 by Jelmer Vernooij
Add context menu in bzrk.
299
    def _treeview_row_mouseclick(self, widget, event):
300
        from bzrlib.plugins.gtk.revisionmenu import RevisionPopupMenu
228 by Jelmer Vernooij
Remove unused code, prefer questions to check boxes.
301
        if event.button == 3:
302
            menu = RevisionPopupMenu(self.branch.repository, 
230 by Jelmer Vernooij
Initial work towards supporting multiple revisions.
303
                [x.revision_id for x in self.selected_revisions()],
304
                self.branch)
228 by Jelmer Vernooij
Remove unused code, prefer questions to check boxes.
305
            menu.popup(None, None, None, event.button, event.get_time())
226 by Jelmer Vernooij
Add context menu in bzrk.
306
307
    def selected_revision(self, path):
308
        return self.model[path][0]
309
310
    def selected_revisions(self):
311
        return [self.selected_revision(path) for path in \
312
                self.treeview.get_selection().get_selected_rows()[1]]
313
47 by Wouter van Heyst
rename _treeview_clicked_cb to correcter _treeview_row_activated_cb
314
    def _treeview_row_activated_cb(self, widget, path, col):
46 by Wouter van Heyst
show diff on row activation, LP# 44591
315
        # TODO: more than one parent
47 by Wouter van Heyst
rename _treeview_clicked_cb to correcter _treeview_row_activated_cb
316
        """Callback for when a treeview row gets activated."""
229 by Jelmer Vernooij
Fix regression.
317
        revision = self.selected_revision(path)
74 by Jelmer Vernooij
Fix exception when double-clicking revision without parents.
318
        if len(self.parent_ids[revision]) == 0:
319
            # Ignore revisions without parent
320
            return
46 by Wouter van Heyst
show diff on row activation, LP# 44591
321
        parent_id = self.parent_ids[revision][0]
152 by Jelmer Vernooij
Cleanup some more code.
322
        self.show_diff(self.branch, revision.revision_id, parent_id)
46 by Wouter van Heyst
show diff on row activation, LP# 44591
323
        self.treeview.grab_focus()