/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
19.1.1 by Erik Bagfors
Support for showing the branch nick
145
        self.table = gtk.Table(rows=5, columns=2)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
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()
19.1.1 by Erik Bagfors
Support for showing the branch nick
185
        label.set_markup("<b>Branch nick:</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
191
        align = gtk.Alignment(0.0, 0.5)
192
        self.branchnick_label = gtk.Label()
193
        self.branchnick_label.set_selectable(True)
194
        align.add(self.branchnick_label)
195
        self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
196
        self.branchnick_label.show()
197
        align.show()
198
199
        align = gtk.Alignment(0.0, 0.5)
200
        label = gtk.Label()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
201
        label.set_markup("<b>Timestamp:</b>")
202
        align.add(label)
19.1.1 by Erik Bagfors
Support for showing the branch nick
203
        self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
204
        label.show()
205
        align.show()
206
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
207
        align = gtk.Alignment(0.0, 0.5)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
208
        self.timestamp_label = gtk.Label()
209
        self.timestamp_label.set_selectable(True)
210
        align.add(self.timestamp_label)
19.1.1 by Erik Bagfors
Support for showing the branch nick
211
        self.table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
212
        self.timestamp_label.show()
213
        align.show()
214
8 by Scott James Remnant
Fiddle around with the alignment and size allocations a little
215
        align = gtk.Alignment(0.0, 0.5)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
216
        label = gtk.Label()
217
        label.set_markup("<b>Parents:</b>")
218
        align.add(label)
19.1.1 by Erik Bagfors
Support for showing the branch nick
219
        self.table.attach(align, 0, 1, 4, 5, gtk.FILL, gtk.FILL)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
220
        label.show()
221
        align.show()
222
223
        self.parents_widgets = []
224
225
        scrollwin = gtk.ScrolledWindow()
226
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
227
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
228
        vbox.pack_start(scrollwin, expand=True, fill=True)
229
        scrollwin.show()
230
231
        self.message_buffer = gtk.TextBuffer()
232
        textview = gtk.TextView(self.message_buffer)
233
        textview.set_editable(False)
234
        textview.set_wrap_mode(gtk.WRAP_WORD)
235
        textview.modify_font(pango.FontDescription("Monospace"))
236
        scrollwin.add(textview)
237
        textview.show()
238
239
        return vbox
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
240
37.1.2 by Robert Collins
Make revision sorting and linking use merge_sorted from latest bzr.dev. This
241
    def set_branch(self, branch, start, robust, maxnum):
1 by Scott James Remnant
Commit the first version of bzrk.
242
        """Set the branch and start position for this window.
243
244
        Creates a new TreeModel and populates it with information about
245
        the new branch before updating the window title and model of the
246
        treeview itself.
247
        """
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
248
        self.branch = branch
249
1 by Scott James Remnant
Commit the first version of bzrk.
250
        # [ 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
251
        self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
252
                                   gobject.TYPE_PYOBJECT,
253
                                   gobject.TYPE_PYOBJECT,
254
                                   gobject.TYPE_PYOBJECT,
255
                                   str, str, str)
256
        self.index = {}
257
        index = 0
1 by Scott James Remnant
Commit the first version of bzrk.
258
259
        last_lines = []
37.1.2 by Robert Collins
Make revision sorting and linking use merge_sorted from latest bzr.dev. This
260
        (self.revisions, colours, self.children, self.parent_ids, merge_sorted) \
261
                 = distances(branch, start, robust, maxnum)
20 by David Allouche
ignore redundent parents
262
        for revision, node, lines in graph(
37.1.3 by Robert Collins
Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.
263
                self.revisions, colours, merge_sorted):
264
            # FIXME: at this point we should be able to show the graph order and
265
            # lines with no message or commit data - and then incrementally fill
266
            # the timestamp, committer etc data as desired.
1 by Scott James Remnant
Commit the first version of bzrk.
267
            message = revision.message.split("\n")[0]
268
            if revision.committer is not None:
269
                timestamp = format_date(revision.timestamp, revision.timezone)
270
            else:
271
                timestamp = None
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
272
            self.model.append([ revision, node, last_lines, lines,
273
                                message, revision.committer, timestamp ])
274
            self.index[revision] = index
275
            index += 1
276
1 by Scott James Remnant
Commit the first version of bzrk.
277
            last_lines = lines
278
36.1.2 by Jamie Wilkinson
put the branch nick in the titlebar instead of the path basename (which didn't work anyway)
279
        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
280
        self.treeview.set_model(self.model)
281
282
    def _treeview_cursor_cb(self, *args):
283
        """Callback for when the treeview cursor changes."""
284
        (path, col) = self.treeview.get_cursor()
285
        revision = self.model[path][0]
286
20 by David Allouche
ignore redundent parents
287
        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
288
        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
289
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
290
        if revision.committer is not None:
37.1.1 by Robert Collins
Retab branchwin.py
291
            branchnick = ""
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
292
            committer = revision.committer
293
            timestamp = format_date(revision.timestamp, revision.timezone)
294
            message = revision.message
37.1.1 by Robert Collins
Retab branchwin.py
295
            try:
296
                branchnick = revision.properties['branch-nick']
297
            except KeyError:
298
                pass
19.1.1 by Erik Bagfors
Support for showing the branch nick
299
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
300
        else:
301
            committer = ""
302
            timestamp = ""
303
            message = ""
37.1.1 by Robert Collins
Retab branchwin.py
304
            branchnick = ""
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
305
306
        self.revid_label.set_text(revision.revision_id)
37.1.1 by Robert Collins
Retab branchwin.py
307
        self.branchnick_label.set_text(branchnick)
19.1.1 by Erik Bagfors
Support for showing the branch nick
308
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
309
        self.committer_label.set_text(committer)
310
        self.timestamp_label.set_text(timestamp)
311
        self.message_buffer.set_text(message)
312
313
        for widget in self.parents_widgets:
314
            self.table.remove(widget)
315
316
        self.parents_widgets = []
34 by David Allouche
[merge conflict] branch nick support
317
        self.table.resize(5 + len(self.parent_ids[revision]) - 1, 2)
20 by David Allouche
ignore redundent parents
318
        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,
319
            self.table.set_row_spacing(idx + 3, 0)
320
321
            align = gtk.Alignment(0.0, 0.0)
322
            self.parents_widgets.append(align)
19.1.1 by Erik Bagfors
Support for showing the branch nick
323
            self.table.attach(align, 1, 2, idx + 4, idx + 5,
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
324
                              gtk.EXPAND | gtk.FILL, gtk.FILL)
325
            align.show()
326
327
            hbox = gtk.HBox(False, 0)
328
            align.add(hbox)
329
            hbox.show()
330
331
            label = gtk.Label(parent_id)
332
            label.set_selectable(True)
333
            hbox.pack_start(label, expand=False, fill=True)
334
            label.show()
335
336
            image = gtk.Image()
337
            image.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
338
            image.show()
339
340
            button = gtk.Button()
341
            button.add(image)
342
            button.set_relief(gtk.RELIEF_NONE)
343
            button.connect("clicked", self._go_clicked_cb, parent_id)
344
            hbox.pack_start(button, expand=False, fill=True)
345
            button.show()
346
347
            image = gtk.Image()
348
            image.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
349
            image.show()
350
351
            button = gtk.Button()
352
            button.add(image)
353
            button.set_relief(gtk.RELIEF_NONE)
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
354
            button.set_sensitive(self.app is not None)
355
            button.connect("clicked", self._show_clicked_cb,
356
                           revision.revision_id, parent_id)
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
357
            hbox.pack_start(button, expand=False, fill=True)
358
            button.show()
359
3 by Scott James Remnant
Split the display in two with a pane, we'll use the bottom half to show
360
    def _back_clicked_cb(self, *args):
361
        """Callback for when the back button is clicked."""
362
        (path, col) = self.treeview.get_cursor()
363
        revision = self.model[path][0]
20 by David Allouche
ignore redundent parents
364
        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
365
            return
366
20 by David Allouche
ignore redundent parents
367
        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
368
            parent = self.revisions[parent_id]
369
            if same_branch(revision, parent):
370
                self.treeview.set_cursor(self.index[parent])
371
                break
372
        else:
20 by David Allouche
ignore redundent parents
373
            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
374
            self.treeview.set_cursor(self.index[next])
13 by Scott James Remnant
Keep the focus on the treeview
375
        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
376
5 by Scott James Remnant
Reverse the meaning of the Back and Forward buttons so Back actually
377
    def _fwd_clicked_cb(self, *args):
378
        """Callback for when the forward button is clicked."""
379
        (path, col) = self.treeview.get_cursor()
380
        revision = self.model[path][0]
381
        if not len(self.children[revision]):
382
            return
383
384
        for child in self.children[revision]:
385
            if same_branch(child, revision):
386
                self.treeview.set_cursor(self.index[child])
387
                break
388
        else:
389
            prev = list(self.children[revision])[0]
390
            self.treeview.set_cursor(self.index[prev])
13 by Scott James Remnant
Keep the focus on the treeview
391
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
392
393
    def _go_clicked_cb(self, widget, revid):
394
        """Callback for when the go button for a parent is clicked."""
395
        self.treeview.set_cursor(self.index[self.revisions[revid]])
13 by Scott James Remnant
Keep the focus on the treeview
396
        self.treeview.grab_focus()
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
397
10 by Scott James Remnant
Add an extra window type, clicking the little icons next to a parent
398
    def _show_clicked_cb(self, widget, revid, parentid):
7 by Scott James Remnant
Put some information about the highlighted revision in the bottom pane,
399
        """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
400
        if self.app is not None:
401
            self.app.show_diff(self.branch, revid, parentid)
13 by Scott James Remnant
Keep the focus on the treeview
402
        self.treeview.grab_focus()