/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
15
16
from bzrlib.osutils import format_date
17
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
18
from graph import distances, graph, same_branch
1 by Scott James Remnant
Commit the first version of bzrk.
19
from graphcell import CellRendererGraph
20
21
22
class BranchWindow(gtk.Window):
23
    """Branch window.
24
25
    This object represents and manages a single window containing information
26
    for a particular branch.
27
    """
28
152 by Jelmer Vernooij
Cleanup some more code.
29
    def __init__(self):
1 by Scott James Remnant
Commit the first version of bzrk.
30
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
31
        self.set_border_width(0)
32
        self.set_title("bzrk")
33
34
        # Use three-quarters of the screen by default
35
        screen = self.get_screen()
4 by Scott James Remnant
Use the size of the first monitor rather than the entire screen
36
        monitor = screen.get_monitor_geometry(0)
37
        width = int(monitor.width * 0.75)
38
        height = int(monitor.height * 0.75)
1 by Scott James Remnant
Commit the first version of bzrk.
39
        self.set_default_size(width, height)
40
41
        # FIXME AndyFitz!
42
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
43
        self.set_icon(icon)
44
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
45
        self.accel_group = gtk.AccelGroup()
46
        self.add_accel_group(self.accel_group)
47
1 by Scott James Remnant
Commit the first version of bzrk.
48
        self.construct()
49
50
    def construct(self):
51
        """Construct the window contents."""
44 by David Allouche
reorganise branch window
52
        vbox = gtk.VBox(spacing=0)
53
        self.add(vbox)
54
55
        vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
56
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
57
        paned = gtk.VPaned()
58
        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,
59
        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
60
        paned.show()
44 by David Allouche
reorganise branch window
61
        vbox.pack_start(paned, expand=True, fill=True)
62
        vbox.set_focus_child(paned)
63
64
        vbox.show()
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
65
66
    def construct_top(self):
67
        """Construct the top-half of the window."""
1 by Scott James Remnant
Commit the first version of bzrk.
68
        scrollwin = gtk.ScrolledWindow()
69
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
70
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
71
        scrollwin.show()
72
73
        self.treeview = gtk.TreeView()
74
        self.treeview.set_rules_hint(True)
75
        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
76
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
47 by Wouter van Heyst
rename _treeview_clicked_cb to correcter _treeview_row_activated_cb
77
        self.treeview.connect("row-activated", self._treeview_row_activated_cb)
1 by Scott James Remnant
Commit the first version of bzrk.
78
        scrollwin.add(self.treeview)
79
        self.treeview.show()
80
81
        cell = CellRendererGraph()
82
        column = gtk.TreeViewColumn()
45 by David Allouche
resizable graph column
83
        column.set_resizable(True)
1 by Scott James Remnant
Commit the first version of bzrk.
84
        column.pack_start(cell, expand=False)
85
        column.add_attribute(cell, "node", 1)
86
        column.add_attribute(cell, "in-lines", 2)
87
        column.add_attribute(cell, "out-lines", 3)
88
        self.treeview.append_column(column)
89
90
        cell = gtk.CellRendererText()
91
        cell.set_property("width-chars", 40)
92
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
93
        column = gtk.TreeViewColumn("Message")
94
        column.set_resizable(True)
95
        column.pack_start(cell, expand=True)
96
        column.add_attribute(cell, "text", 4)
97
        self.treeview.append_column(column)
98
99
        cell = gtk.CellRendererText()
100
        cell.set_property("width-chars", 40)
101
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
102
        column = gtk.TreeViewColumn("Committer")
103
        column.set_resizable(True)
104
        column.pack_start(cell, expand=True)
105
        column.add_attribute(cell, "text", 5)
106
        self.treeview.append_column(column)
107
108
        cell = gtk.CellRendererText()
109
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
110
        column = gtk.TreeViewColumn("Date")
111
        column.set_resizable(True)
112
        column.pack_start(cell, expand=True)
113
        column.add_attribute(cell, "text", 6)
114
        self.treeview.append_column(column)
115
44 by David Allouche
reorganise branch window
116
        return scrollwin
117
118
    def construct_navigation(self):
119
        """Construct the navigation buttons."""
120
        frame = gtk.Frame()
121
        frame.set_shadow_type(gtk.SHADOW_OUT)
122
        frame.show()
123
        
124
        hbox = gtk.HBox(spacing=12)
125
        frame.add(hbox)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
126
        hbox.show()
127
128
        self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
44 by David Allouche
reorganise branch window
129
        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
130
        self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
131
                                         0, 0)
132
        self.back_button.connect("clicked", self._back_clicked_cb)
133
        hbox.pack_start(self.back_button, expand=False, fill=True)
134
        self.back_button.show()
135
136
        self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
44 by David Allouche
reorganise branch window
137
        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
138
        self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
139
                                        0, 0)
140
        self.fwd_button.connect("clicked", self._fwd_clicked_cb)
141
        hbox.pack_start(self.fwd_button, expand=False, fill=True)
142
        self.fwd_button.show()
143
44 by David Allouche
reorganise branch window
144
        return frame
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
145
146
    def construct_bottom(self):
147
        """Construct the bottom half of the window."""
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
148
        from bzrlib.plugins.gtk.logview import LogView
149
        self.logview = LogView()
44 by David Allouche
reorganise branch window
150
        (width, height) = self.get_size()
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
151
        self.logview.set_size_request(width, int(height / 2.5))
152
        self.logview.show()
153
        self.logview.set_show_callback(self._show_clicked_cb)
154
        self.logview.set_go_callback(self._go_clicked_cb)
155
        return self.logview
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
156
40 by David Allouche
remove --robust, pyflakes fixes, update README
157
    def set_branch(self, branch, start, maxnum):
1 by Scott James Remnant
Commit the first version of bzrk.
158
        """Set the branch and start position for this window.
159
160
        Creates a new TreeModel and populates it with information about
161
        the new branch before updating the window title and model of the
162
        treeview itself.
163
        """
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
164
        self.branch = branch
165
1 by Scott James Remnant
Commit the first version of bzrk.
166
        # [ revision, node, last_lines, lines, message, committer, timestamp ]
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
167
        self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
168
                                   gobject.TYPE_PYOBJECT,
169
                                   gobject.TYPE_PYOBJECT,
170
                                   gobject.TYPE_PYOBJECT,
171
                                   str, str, str)
172
        self.index = {}
173
        index = 0
1 by Scott James Remnant
Commit the first version of bzrk.
174
175
        last_lines = []
40 by David Allouche
remove --robust, pyflakes fixes, update README
176
        (self.revisions, colours, self.children, self.parent_ids,
41 by David Allouche
restore --maxnum functionality, reflush some comments
177
         merge_sorted) = distances(branch, start)
178
        for (index, (revision, node, lines)) in enumerate(graph(
179
                self.revisions, colours, merge_sorted)):
180
            # FIXME: at this point we should be able to show the graph order
181
            # and lines with no message or commit data - and then incrementally
182
            # fill the timestamp, committer etc data as desired.
1 by Scott James Remnant
Commit the first version of bzrk.
183
            message = revision.message.split("\n")[0]
184
            if revision.committer is not None:
185
                timestamp = format_date(revision.timestamp, revision.timezone)
186
            else:
187
                timestamp = None
41 by David Allouche
restore --maxnum functionality, reflush some comments
188
            self.model.append([revision, node, last_lines, lines,
189
                               message, revision.committer, timestamp])
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
190
            self.index[revision] = index
1 by Scott James Remnant
Commit the first version of bzrk.
191
            last_lines = lines
41 by David Allouche
restore --maxnum functionality, reflush some comments
192
            if maxnum is not None and index > maxnum:
193
                break
1 by Scott James Remnant
Commit the first version of bzrk.
194
36.1.2 by Jamie Wilkinson
put the branch nick in the titlebar instead of the path basename (which didn't work anyway)
195
        self.set_title(branch.nick + " - bzrk")
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
196
        self.treeview.set_model(self.model)
197
198
    def _treeview_cursor_cb(self, *args):
199
        """Callback for when the treeview cursor changes."""
200
        (path, col) = self.treeview.get_cursor()
201
        revision = self.model[path][0]
202
20 by David Allouche
ignore redundent parents
203
        self.back_button.set_sensitive(len(self.parent_ids[revision]) > 0)
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
204
        self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
205
        self.logview.set_revision(revision)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
206
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
207
    def _back_clicked_cb(self, *args):
208
        """Callback for when the back button is clicked."""
209
        (path, col) = self.treeview.get_cursor()
210
        revision = self.model[path][0]
20 by David Allouche
ignore redundent parents
211
        if not len(self.parent_ids[revision]):
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
212
            return
213
20 by David Allouche
ignore redundent parents
214
        for parent_id in self.parent_ids[revision]:
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
215
            parent = self.revisions[parent_id]
216
            if same_branch(revision, parent):
217
                self.treeview.set_cursor(self.index[parent])
218
                break
219
        else:
20 by David Allouche
ignore redundent parents
220
            next = self.revisions[self.parent_ids[revision][0]]
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
221
            self.treeview.set_cursor(self.index[next])
13 by Scott James Remnant
Keep the focus on the treeview
222
        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
223
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
224
    def _fwd_clicked_cb(self, *args):
225
        """Callback for when the forward button is clicked."""
226
        (path, col) = self.treeview.get_cursor()
227
        revision = self.model[path][0]
228
        if not len(self.children[revision]):
229
            return
230
231
        for child in self.children[revision]:
232
            if same_branch(child, revision):
233
                self.treeview.set_cursor(self.index[child])
234
                break
235
        else:
236
            prev = list(self.children[revision])[0]
237
            self.treeview.set_cursor(self.index[prev])
13 by Scott James Remnant
Keep the focus on the treeview
238
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
239
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
240
    def _go_clicked_cb(self, revid):
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
241
        """Callback for when the go button for a parent is clicked."""
242
        self.treeview.set_cursor(self.index[self.revisions[revid]])
13 by Scott James Remnant
Keep the focus on the treeview
243
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
244
152 by Jelmer Vernooij
Cleanup some more code.
245
    def show_diff(self, branch, revid, parentid):
246
        """Open a new window to show a diff between the given revisions."""
247
        from bzrlib.plugins.gtk.diff import DiffWindow
248
        window = DiffWindow()
249
        rev_tree = branch.repository.revision_tree(revid)
250
        parent_tree = branch.repository.revision_tree(parentid)
251
        description = revid + " - " + branch.nick
252
        window.set_diff(description, rev_tree, parent_tree)
253
        window.show()
254
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
255
    def _show_clicked_cb(self, revid, parentid):
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
256
        """Callback for when the show button for a parent is clicked."""
152 by Jelmer Vernooij
Cleanup some more code.
257
        self.show_diff(self.branch, revid, parentid)
13 by Scott James Remnant
Keep the focus on the treeview
258
        self.treeview.grab_focus()
46 by Wouter van Heyst
show diff on row activation, LP# 44591
259
47 by Wouter van Heyst
rename _treeview_clicked_cb to correcter _treeview_row_activated_cb
260
    def _treeview_row_activated_cb(self, widget, path, col):
46 by Wouter van Heyst
show diff on row activation, LP# 44591
261
        # TODO: more than one parent
47 by Wouter van Heyst
rename _treeview_clicked_cb to correcter _treeview_row_activated_cb
262
        """Callback for when a treeview row gets activated."""
46 by Wouter van Heyst
show diff on row activation, LP# 44591
263
        revision = self.model[path][0]
74 by Jelmer Vernooij
Fix exception when double-clicking revision without parents.
264
        if len(self.parent_ids[revision]) == 0:
265
            # Ignore revisions without parent
266
            return
46 by Wouter van Heyst
show diff on row activation, LP# 44591
267
        parent_id = self.parent_ids[revision][0]
152 by Jelmer Vernooij
Cleanup some more code.
268
        self.show_diff(self.branch, revision.revision_id, parent_id)
46 by Wouter van Heyst
show diff on row activation, LP# 44591
269
        self.treeview.grab_focus()