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