/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)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
82
        self.treeview.connect("cursor-changed", self._treeview_cursor_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)
1 by Scott James Remnant
Commit the first version of bzrk.
86
        scrollwin.add(self.treeview)
87
        self.treeview.show()
88
256.2.27 by Gary van der Merwe
Show Revision No in tree
89
        cell = gtk.CellRendererText()
90
        #cell.set_property("width-chars", 40)
91
        #cell.set_property("ellipsize", pango.ELLIPSIZE_END)
92
        column = gtk.TreeViewColumn("Revision No")
93
        column.set_resizable(True)
94
        column.pack_start(cell, expand=True)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
95
        column.add_attribute(cell, "text", treemodel.REVNO)
256.2.27 by Gary van der Merwe
Show Revision No in tree
96
        self.treeview.append_column(column)
97
1 by Scott James Remnant
Commit the first version of bzrk.
98
        cell = CellRendererGraph()
99
        column = gtk.TreeViewColumn()
45 by David Allouche
resizable graph column
100
        column.set_resizable(True)
1 by Scott James Remnant
Commit the first version of bzrk.
101
        column.pack_start(cell, expand=False)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
102
        column.add_attribute(cell, "node", treemodel.NODE)
103
        column.add_attribute(cell, "in-lines", treemodel.LAST_LINES)
104
        column.add_attribute(cell, "out-lines", treemodel.LINES)
1 by Scott James Remnant
Commit the first version of bzrk.
105
        self.treeview.append_column(column)
106
107
        cell = gtk.CellRendererText()
108
        cell.set_property("width-chars", 40)
109
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
110
        column = gtk.TreeViewColumn("Message")
111
        column.set_resizable(True)
112
        column.pack_start(cell, expand=True)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
113
        column.add_attribute(cell, "text", treemodel.MESSAGE)
1 by Scott James Remnant
Commit the first version of bzrk.
114
        self.treeview.append_column(column)
115
116
        cell = gtk.CellRendererText()
117
        cell.set_property("width-chars", 40)
118
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
119
        column = gtk.TreeViewColumn("Committer")
120
        column.set_resizable(True)
121
        column.pack_start(cell, expand=True)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
122
        column.add_attribute(cell, "text", treemodel.COMMITER)
1 by Scott James Remnant
Commit the first version of bzrk.
123
        self.treeview.append_column(column)
124
125
        cell = gtk.CellRendererText()
126
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
127
        column = gtk.TreeViewColumn("Date")
128
        column.set_resizable(True)
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.TIMESTAMP)
1 by Scott James Remnant
Commit the first version of bzrk.
131
        self.treeview.append_column(column)
132
44 by David Allouche
reorganise branch window
133
        return scrollwin
134
135
    def construct_navigation(self):
136
        """Construct the navigation buttons."""
137
        frame = gtk.Frame()
138
        frame.set_shadow_type(gtk.SHADOW_OUT)
139
        frame.show()
140
        
141
        hbox = gtk.HBox(spacing=12)
142
        frame.add(hbox)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
143
        hbox.show()
144
145
        self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
44 by David Allouche
reorganise branch window
146
        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
147
        self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
148
                                         0, 0)
149
        self.back_button.connect("clicked", self._back_clicked_cb)
150
        hbox.pack_start(self.back_button, expand=False, fill=True)
151
        self.back_button.show()
152
153
        self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
44 by David Allouche
reorganise branch window
154
        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
155
        self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
156
                                        0, 0)
157
        self.fwd_button.connect("clicked", self._fwd_clicked_cb)
158
        hbox.pack_start(self.fwd_button, expand=False, fill=True)
159
        self.fwd_button.show()
160
44 by David Allouche
reorganise branch window
161
        return frame
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
162
163
    def construct_bottom(self):
164
        """Construct the bottom half of the window."""
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
165
        from bzrlib.plugins.gtk.logview import LogView
256.2.23 by Gary van der Merwe
Show Children
166
        self.logview = LogView(None, True, [], True)
44 by David Allouche
reorganise branch window
167
        (width, height) = self.get_size()
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
168
        self.logview.set_size_request(width, int(height / 2.5))
169
        self.logview.show()
170
        self.logview.set_show_callback(self._show_clicked_cb)
171
        self.logview.set_go_callback(self._go_clicked_cb)
172
        return self.logview
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
173
40 by David Allouche
remove --robust, pyflakes fixes, update README
174
    def set_branch(self, branch, start, maxnum):
1 by Scott James Remnant
Commit the first version of bzrk.
175
        """Set the branch and start position for this window.
176
177
        Creates a new TreeModel and populates it with information about
178
        the new branch before updating the window title and model of the
179
        treeview itself.
180
        """
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
181
        self.branch = branch
265.1.1 by Gary van der Merwe
Show bzr viz interface quickly.
182
        self.set_title(branch.nick + " - bzrk")
183
        gobject.idle_add(self.populate_model, start, maxnum)
184
185
    def populate_model(self, start, maxnum):
256.2.28 by Gary van der Merwe
Better line drawing with info from merge_sort
186
        (linegraphdata, index) = linegraph(self.branch,
187
                                           start,
188
                                           maxnum)
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
189
        self.model = TreeModel(self.branch, linegraphdata)
256.2.21 by Gary van der Merwe
Refactor so that line graph is more contained.
190
        self.index = index
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
191
        self.treeview.set_model(self.model)
275.1.5 by Daniel Schierbeck
Made windows close correctly on Ctrl-W and made the application quit on Ctrl-Q.
192
    def _on_key_pressed(self, widget, event):
193
        """ Key press event handler. """
194
        keyname = gtk.gdk.keyval_name(event.keyval)
195
        func = getattr(self, '_on_key_press_' + keyname, None)
196
        if func:
197
            return func(event)
198
199
    def _on_key_press_w(self, event):
200
        if event.state & gtk.gdk.CONTROL_MASK:
201
            self.destroy()
275.1.6 by Daniel Schierbeck
Made Ctrl-W call gtk.main_quit if the window has no parent.
202
            if self._parent is None:
203
                gtk.main_quit()
275.1.5 by Daniel Schierbeck
Made windows close correctly on Ctrl-W and made the application quit on Ctrl-Q.
204
205
    def _on_key_press_q(self, event):
206
        if event.state & gtk.gdk.CONTROL_MASK:
207
            gtk.main_quit()
265.1.1 by Gary van der Merwe
Show bzr viz interface quickly.
208
    
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
209
    def _treeview_cursor_cb(self, *args):
210
        """Callback for when the treeview cursor changes."""
211
        (path, col) = self.treeview.get_cursor()
256.2.29 by Gary van der Merwe
Implement a TreeModel that loads revisions incrementaly.
212
        iter = self.model.get_iter(path)
213
        revision = self.model.get_value(iter, treemodel.REVISION)
214
        parents = self.model.get_value(iter, treemodel.PARENTS)
215
        children = self.model.get_value(iter, treemodel.CHILDREN)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
216
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
217
        self.back_button.set_sensitive(len(parents) > 0)
218
        self.fwd_button.set_sensitive(len(children) > 0)
241 by Jelmer Vernooij
Show tags in bzr viz.
219
        tags = []
220
        if self.branch.supports_tags():
221
            tagdict = self.branch.tags.get_reverse_tag_dict()
222
            if tagdict.has_key(revision.revision_id):
223
                tags = tagdict[revision.revision_id]
256.2.23 by Gary van der Merwe
Show Children
224
        self.logview.set_revision(revision, tags, children)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
225
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
226
    def _back_clicked_cb(self, *args):
227
        """Callback for when the back button is clicked."""
228
        (path, col) = self.treeview.get_cursor()
229
        revision = self.model[path][0]
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
230
        parents = self.model[path][4]
231
        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
232
            return
233
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
234
        for parent_id in parents:
235
            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
236
            if same_branch(revision, parent):
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
237
                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
238
                break
239
        else:
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
240
            self.treeview.set_cursor(self.index[parents[0]])
13 by Scott James Remnant
Keep the focus on the treeview
241
        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
242
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
243
    def _fwd_clicked_cb(self, *args):
244
        """Callback for when the forward button is clicked."""
245
        (path, col) = self.treeview.get_cursor()
246
        revision = self.model[path][0]
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
247
        children = self.model[path][5]
248
        if not len(children):
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
249
            return
250
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
251
        for child_id in children:
252
            child = self.revisions[self.index[child_id]]
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
253
            if same_branch(child, revision):
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
254
                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
255
                break
256
        else:
256.2.4 by Gary van der Merwe
First go at using bzrlib graph code
257
            self.treeview.set_cursor(self.index[children[0]])
13 by Scott James Remnant
Keep the focus on the treeview
258
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
259
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
260
    def _go_clicked_cb(self, revid):
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
261
        """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
262
        self.treeview.set_cursor(self.index[revid])
13 by Scott James Remnant
Keep the focus on the treeview
263
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
264
152 by Jelmer Vernooij
Cleanup some more code.
265
    def show_diff(self, branch, revid, parentid):
266
        """Open a new window to show a diff between the given revisions."""
267
        from bzrlib.plugins.gtk.diff import DiffWindow
268
        window = DiffWindow()
226 by Jelmer Vernooij
Add context menu in bzrk.
269
        (parent_tree, rev_tree) = branch.repository.revision_trees([parentid, 
270
                                                                   revid])
152 by Jelmer Vernooij
Cleanup some more code.
271
        description = revid + " - " + branch.nick
272
        window.set_diff(description, rev_tree, parent_tree)
273
        window.show()
274
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
275
    def _show_clicked_cb(self, revid, parentid):
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
276
        """Callback for when the show button for a parent is clicked."""
152 by Jelmer Vernooij
Cleanup some more code.
277
        self.show_diff(self.branch, revid, parentid)
13 by Scott James Remnant
Keep the focus on the treeview
278
        self.treeview.grab_focus()
46 by Wouter van Heyst
show diff on row activation, LP# 44591
279
226 by Jelmer Vernooij
Add context menu in bzrk.
280
    def _treeview_row_mouseclick(self, widget, event):
281
        from bzrlib.plugins.gtk.revisionmenu import RevisionPopupMenu
228 by Jelmer Vernooij
Remove unused code, prefer questions to check boxes.
282
        if event.button == 3:
283
            menu = RevisionPopupMenu(self.branch.repository, 
230 by Jelmer Vernooij
Initial work towards supporting multiple revisions.
284
                [x.revision_id for x in self.selected_revisions()],
285
                self.branch)
228 by Jelmer Vernooij
Remove unused code, prefer questions to check boxes.
286
            menu.popup(None, None, None, event.button, event.get_time())
226 by Jelmer Vernooij
Add context menu in bzrk.
287
288
    def selected_revision(self, path):
289
        return self.model[path][0]
290
291
    def selected_revisions(self):
292
        return [self.selected_revision(path) for path in \
293
                self.treeview.get_selection().get_selected_rows()[1]]
294
47 by Wouter van Heyst
rename _treeview_clicked_cb to correcter _treeview_row_activated_cb
295
    def _treeview_row_activated_cb(self, widget, path, col):
46 by Wouter van Heyst
show diff on row activation, LP# 44591
296
        # TODO: more than one parent
47 by Wouter van Heyst
rename _treeview_clicked_cb to correcter _treeview_row_activated_cb
297
        """Callback for when a treeview row gets activated."""
229 by Jelmer Vernooij
Fix regression.
298
        revision = self.selected_revision(path)
74 by Jelmer Vernooij
Fix exception when double-clicking revision without parents.
299
        if len(self.parent_ids[revision]) == 0:
300
            # Ignore revisions without parent
301
            return
46 by Wouter van Heyst
show diff on row activation, LP# 44591
302
        parent_id = self.parent_ids[revision][0]
152 by Jelmer Vernooij
Cleanup some more code.
303
        self.show_diff(self.branch, revision.revision_id, parent_id)
46 by Wouter van Heyst
show diff on row activation, LP# 44591
304
        self.treeview.grab_focus()