/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
#!/usr/bin/python
2
# -*- coding: UTF-8 -*-
3
"""Branch window.
4
5
This module contains the code to manage the branch information window,
6
which contains both the revision graph and details panes.
7
"""
8
9
__copyright__ = "Copyright © 2005 Canonical Ltd."
10
__author__    = "Scott James Remnant <scott@ubuntu.com>"
11
12
13
import os
14
15
import gtk
16
import gobject
17
import pango
18
19
from bzrlib.osutils import format_date
20
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
21
from graph import distances, graph, same_branch
1 by Scott James Remnant
Commit the first version of bzrk.
22
from graphcell import CellRendererGraph
23
24
25
class BranchWindow(gtk.Window):
26
    """Branch window.
27
28
    This object represents and manages a single window containing information
29
    for a particular branch.
30
    """
31
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
32
    def __init__(self, app=None):
1 by Scott James Remnant
Commit the first version of bzrk.
33
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
34
        self.set_border_width(0)
35
        self.set_title("bzrk")
36
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
37
        self.app = app
38
1 by Scott James Remnant
Commit the first version of bzrk.
39
        # Use three-quarters of the screen by default
40
        screen = self.get_screen()
4 by Scott James Remnant
Use the size of the first monitor rather than the entire screen
41
        monitor = screen.get_monitor_geometry(0)
42
        width = int(monitor.width * 0.75)
43
        height = int(monitor.height * 0.75)
1 by Scott James Remnant
Commit the first version of bzrk.
44
        self.set_default_size(width, height)
45
46
        # FIXME AndyFitz!
47
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
48
        self.set_icon(icon)
49
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
50
        self.accel_group = gtk.AccelGroup()
51
        self.add_accel_group(self.accel_group)
52
1 by Scott James Remnant
Commit the first version of bzrk.
53
        self.construct()
54
55
    def construct(self):
56
        """Construct the window contents."""
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
        self.add(paned)
61
        paned.show()
62
63
    def construct_top(self):
64
        """Construct the top-half of the window."""
65
        vbox = gtk.VBox(spacing=6)
66
        vbox.set_border_width(12)
67
        vbox.show()
68
1 by Scott James Remnant
Commit the first version of bzrk.
69
        scrollwin = gtk.ScrolledWindow()
70
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
71
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
72
        vbox.pack_start(scrollwin, expand=True, fill=True)
1 by Scott James Remnant
Commit the first version of bzrk.
73
        scrollwin.show()
74
75
        self.treeview = gtk.TreeView()
76
        self.treeview.set_rules_hint(True)
77
        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
78
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
1 by Scott James Remnant
Commit the first version of bzrk.
79
        scrollwin.add(self.treeview)
80
        self.treeview.show()
81
82
        cell = CellRendererGraph()
83
        column = gtk.TreeViewColumn()
84
        column.set_resizable(False)
85
        column.pack_start(cell, expand=False)
86
        column.add_attribute(cell, "node", 1)
87
        column.add_attribute(cell, "in-lines", 2)
88
        column.add_attribute(cell, "out-lines", 3)
89
        self.treeview.append_column(column)
90
91
        cell = gtk.CellRendererText()
92
        cell.set_property("width-chars", 40)
93
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
94
        column = gtk.TreeViewColumn("Message")
95
        column.set_resizable(True)
96
        column.pack_start(cell, expand=True)
97
        column.add_attribute(cell, "text", 4)
98
        self.treeview.append_column(column)
99
100
        cell = gtk.CellRendererText()
101
        cell.set_property("width-chars", 40)
102
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
103
        column = gtk.TreeViewColumn("Committer")
104
        column.set_resizable(True)
105
        column.pack_start(cell, expand=True)
106
        column.add_attribute(cell, "text", 5)
107
        self.treeview.append_column(column)
108
109
        cell = gtk.CellRendererText()
110
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
111
        column = gtk.TreeViewColumn("Date")
112
        column.set_resizable(True)
113
        column.pack_start(cell, expand=True)
114
        column.add_attribute(cell, "text", 6)
115
        self.treeview.append_column(column)
116
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
117
        hbox = gtk.HBox(False, spacing=6)
118
        vbox.pack_start(hbox, expand=False, fill=False)
119
        hbox.show()
120
121
        self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
122
        self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
123
                                         0, 0)
124
        self.back_button.connect("clicked", self._back_clicked_cb)
125
        hbox.pack_start(self.back_button, expand=False, fill=True)
126
        self.back_button.show()
127
128
        self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
129
        self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
130
                                        0, 0)
131
        self.fwd_button.connect("clicked", self._fwd_clicked_cb)
132
        hbox.pack_start(self.fwd_button, expand=False, fill=True)
133
        self.fwd_button.show()
134
135
        return vbox
136
137
    def construct_bottom(self):
138
        """Construct the bottom half of the window."""
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
139
        vbox = gtk.VBox(False, spacing=6)
140
        vbox.set_border_width(12)
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
141
        (width, height) = self.get_size()
142
        vbox.set_size_request(width, int(height / 2.5))
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
143
        vbox.show()
144
145
        self.table = gtk.Table(rows=4, columns=2)
146
        self.table.set_row_spacings(6)
147
        self.table.set_col_spacings(6)
148
        vbox.pack_start(self.table, expand=False, fill=True)
149
        self.table.show()
150
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
151
        align = gtk.Alignment(0.0, 0.5)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
152
        label = gtk.Label()
153
        label.set_markup("<b>Revision:</b>")
154
        align.add(label)
155
        self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
156
        label.show()
157
        align.show()
158
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
159
        align = gtk.Alignment(0.0, 0.5)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
160
        self.revid_label = gtk.Label()
161
        self.revid_label.set_selectable(True)
162
        align.add(self.revid_label)
163
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
164
        self.revid_label.show()
165
        align.show()
166
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
167
        align = gtk.Alignment(0.0, 0.5)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
168
        label = gtk.Label()
169
        label.set_markup("<b>Committer:</b>")
170
        align.add(label)
171
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
172
        label.show()
173
        align.show()
174
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
175
        align = gtk.Alignment(0.0, 0.5)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
176
        self.committer_label = gtk.Label()
177
        self.committer_label.set_selectable(True)
178
        align.add(self.committer_label)
179
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
180
        self.committer_label.show()
181
        align.show()
182
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
183
        align = gtk.Alignment(0.0, 0.5)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
184
        label = gtk.Label()
185
        label.set_markup("<b>Timestamp:</b>")
186
        align.add(label)
187
        self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
188
        label.show()
189
        align.show()
190
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
191
        align = gtk.Alignment(0.0, 0.5)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
192
        self.timestamp_label = gtk.Label()
193
        self.timestamp_label.set_selectable(True)
194
        align.add(self.timestamp_label)
195
        self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
196
        self.timestamp_label.show()
197
        align.show()
198
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
199
        align = gtk.Alignment(0.0, 0.5)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
200
        label = gtk.Label()
201
        label.set_markup("<b>Parents:</b>")
202
        align.add(label)
203
        self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
204
        label.show()
205
        align.show()
206
207
        self.parents_widgets = []
208
209
        scrollwin = gtk.ScrolledWindow()
210
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
211
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
212
        vbox.pack_start(scrollwin, expand=True, fill=True)
213
        scrollwin.show()
214
215
        self.message_buffer = gtk.TextBuffer()
216
        textview = gtk.TextView(self.message_buffer)
217
        textview.set_editable(False)
218
        textview.set_wrap_mode(gtk.WRAP_WORD)
219
        textview.modify_font(pango.FontDescription("Monospace"))
220
        scrollwin.add(textview)
221
        textview.show()
222
223
        return vbox
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
224
1 by Scott James Remnant
Commit the first version of bzrk.
225
    def set_branch(self, branch, start):
226
        """Set the branch and start position for this window.
227
228
        Creates a new TreeModel and populates it with information about
229
        the new branch before updating the window title and model of the
230
        treeview itself.
231
        """
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
232
        self.branch = branch
233
1 by Scott James Remnant
Commit the first version of bzrk.
234
        # [ 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
235
        self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
236
                                   gobject.TYPE_PYOBJECT,
237
                                   gobject.TYPE_PYOBJECT,
238
                                   gobject.TYPE_PYOBJECT,
239
                                   str, str, str)
240
        self.index = {}
241
        index = 0
1 by Scott James Remnant
Commit the first version of bzrk.
242
243
        last_lines = []
20 by David Allouche
ignore redundent parents
244
        (revids, self.revisions, colours, self.children, self.parent_ids) \
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
245
                 = distances(branch, start)
20 by David Allouche
ignore redundent parents
246
        for revision, node, lines in graph(
247
                revids, self.revisions, colours, self.parent_ids):
1 by Scott James Remnant
Commit the first version of bzrk.
248
            message = revision.message.split("\n")[0]
249
            if revision.committer is not None:
250
                timestamp = format_date(revision.timestamp, revision.timezone)
251
            else:
252
                timestamp = None
253
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
254
            self.model.append([ revision, node, last_lines, lines,
255
                                message, revision.committer, timestamp ])
256
            self.index[revision] = index
257
            index += 1
258
1 by Scott James Remnant
Commit the first version of bzrk.
259
            last_lines = lines
260
261
        self.set_title(os.path.basename(branch.base) + " - bzrk")
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
262
        self.treeview.set_model(self.model)
263
264
    def _treeview_cursor_cb(self, *args):
265
        """Callback for when the treeview cursor changes."""
266
        (path, col) = self.treeview.get_cursor()
267
        revision = self.model[path][0]
268
20 by David Allouche
ignore redundent parents
269
        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
270
        self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
271
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
272
        if revision.committer is not None:
273
            committer = revision.committer
274
            timestamp = format_date(revision.timestamp, revision.timezone)
275
            message = revision.message
276
        else:
277
            committer = ""
278
            timestamp = ""
279
            message = ""
280
281
        self.revid_label.set_text(revision.revision_id)
282
        self.committer_label.set_text(committer)
283
        self.timestamp_label.set_text(timestamp)
284
        self.message_buffer.set_text(message)
285
286
        for widget in self.parents_widgets:
287
            self.table.remove(widget)
288
289
        self.parents_widgets = []
20 by David Allouche
ignore redundent parents
290
        self.table.resize(4 + len(self.parent_ids[revision]) - 1, 2)
291
        for idx, parent_id in enumerate(self.parent_ids[revision]):
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
292
            self.table.set_row_spacing(idx + 3, 0)
293
294
            align = gtk.Alignment(0.0, 0.0)
295
            self.parents_widgets.append(align)
296
            self.table.attach(align, 1, 2, idx + 3, idx + 4,
297
                              gtk.EXPAND | gtk.FILL, gtk.FILL)
298
            align.show()
299
300
            hbox = gtk.HBox(False, 0)
301
            align.add(hbox)
302
            hbox.show()
303
304
            label = gtk.Label(parent_id)
305
            label.set_selectable(True)
306
            hbox.pack_start(label, expand=False, fill=True)
307
            label.show()
308
309
            image = gtk.Image()
310
            image.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
311
            image.show()
312
313
            button = gtk.Button()
314
            button.add(image)
315
            button.set_relief(gtk.RELIEF_NONE)
316
            button.connect("clicked", self._go_clicked_cb, parent_id)
317
            hbox.pack_start(button, expand=False, fill=True)
318
            button.show()
319
320
            image = gtk.Image()
321
            image.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
322
            image.show()
323
324
            button = gtk.Button()
325
            button.add(image)
326
            button.set_relief(gtk.RELIEF_NONE)
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
327
            button.set_sensitive(self.app is not None)
328
            button.connect("clicked", self._show_clicked_cb,
329
                           revision.revision_id, parent_id)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
330
            hbox.pack_start(button, expand=False, fill=True)
331
            button.show()
332
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
333
    def _back_clicked_cb(self, *args):
334
        """Callback for when the back button is clicked."""
335
        (path, col) = self.treeview.get_cursor()
336
        revision = self.model[path][0]
20 by David Allouche
ignore redundent parents
337
        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
338
            return
339
20 by David Allouche
ignore redundent parents
340
        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
341
            parent = self.revisions[parent_id]
342
            if same_branch(revision, parent):
343
                self.treeview.set_cursor(self.index[parent])
344
                break
345
        else:
20 by David Allouche
ignore redundent parents
346
            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
347
            self.treeview.set_cursor(self.index[next])
13 by Scott James Remnant
Keep the focus on the treeview
348
        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
349
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
350
    def _fwd_clicked_cb(self, *args):
351
        """Callback for when the forward button is clicked."""
352
        (path, col) = self.treeview.get_cursor()
353
        revision = self.model[path][0]
354
        if not len(self.children[revision]):
355
            return
356
357
        for child in self.children[revision]:
358
            if same_branch(child, revision):
359
                self.treeview.set_cursor(self.index[child])
360
                break
361
        else:
362
            prev = list(self.children[revision])[0]
363
            self.treeview.set_cursor(self.index[prev])
13 by Scott James Remnant
Keep the focus on the treeview
364
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
365
366
    def _go_clicked_cb(self, widget, revid):
367
        """Callback for when the go button for a parent is clicked."""
368
        self.treeview.set_cursor(self.index[self.revisions[revid]])
13 by Scott James Remnant
Keep the focus on the treeview
369
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
370
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
371
    def _show_clicked_cb(self, widget, revid, parentid):
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
372
        """Callback for when the show button for a parent is clicked."""
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
373
        if self.app is not None:
374
            self.app.show_diff(self.branch, revid, parentid)
13 by Scott James Remnant
Keep the focus on the treeview
375
        self.treeview.grab_focus()