/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to viz/branchwin.py

  • Committer: Jelmer Vernooij
  • Date: 2007-02-01 15:50:40 UTC
  • Revision ID: jelmer@samba.org-20070201155040-3hq4mfbxs99kzazy
add framework for tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
1
# -*- coding: UTF-8 -*-
3
2
"""Branch window.
4
3
 
10
9
__author__    = "Scott James Remnant <scott@ubuntu.com>"
11
10
 
12
11
 
13
 
import os
14
 
 
15
12
import gtk
16
13
import gobject
17
14
import pango
18
15
 
19
16
from bzrlib.osutils import format_date
20
17
 
21
 
from graph import graph
 
18
from graph import distances, graph, same_branch
22
19
from graphcell import CellRendererGraph
23
20
 
24
21
 
29
26
    for a particular branch.
30
27
    """
31
28
 
32
 
    def __init__(self):
 
29
    def __init__(self, app=None):
33
30
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
34
31
        self.set_border_width(0)
35
32
        self.set_title("bzrk")
36
33
 
 
34
        self.app = app
 
35
 
37
36
        # Use three-quarters of the screen by default
38
37
        screen = self.get_screen()
39
 
        width = int(screen.get_width() * 0.75)
40
 
        height = int(screen.get_height() * 0.75)
 
38
        monitor = screen.get_monitor_geometry(0)
 
39
        width = int(monitor.width * 0.75)
 
40
        height = int(monitor.height * 0.75)
41
41
        self.set_default_size(width, height)
42
42
 
43
43
        # FIXME AndyFitz!
44
44
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
45
45
        self.set_icon(icon)
46
46
 
 
47
        self.accel_group = gtk.AccelGroup()
 
48
        self.add_accel_group(self.accel_group)
 
49
 
47
50
        self.construct()
48
51
 
49
52
    def construct(self):
50
53
        """Construct the window contents."""
 
54
        vbox = gtk.VBox(spacing=0)
 
55
        self.add(vbox)
 
56
 
 
57
        vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
 
58
 
 
59
        paned = gtk.VPaned()
 
60
        paned.pack1(self.construct_top(), resize=True, shrink=False)
 
61
        paned.pack2(self.construct_bottom(), resize=False, shrink=True)
 
62
        paned.show()
 
63
        vbox.pack_start(paned, expand=True, fill=True)
 
64
        vbox.set_focus_child(paned)
 
65
 
 
66
        vbox.show()
 
67
 
 
68
    def construct_top(self):
 
69
        """Construct the top-half of the window."""
51
70
        scrollwin = gtk.ScrolledWindow()
52
71
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
53
72
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
54
 
        scrollwin.set_border_width(12)
55
 
        self.add(scrollwin)
56
73
        scrollwin.show()
57
74
 
58
75
        self.treeview = gtk.TreeView()
59
76
        self.treeview.set_rules_hint(True)
60
77
        self.treeview.set_search_column(4)
 
78
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
 
79
        self.treeview.connect("row-activated", self._treeview_row_activated_cb)
61
80
        scrollwin.add(self.treeview)
62
81
        self.treeview.show()
63
82
 
64
83
        cell = CellRendererGraph()
65
84
        column = gtk.TreeViewColumn()
66
 
        column.set_resizable(False)
 
85
        column.set_resizable(True)
67
86
        column.pack_start(cell, expand=False)
68
87
        column.add_attribute(cell, "node", 1)
69
88
        column.add_attribute(cell, "in-lines", 2)
96
115
        column.add_attribute(cell, "text", 6)
97
116
        self.treeview.append_column(column)
98
117
 
99
 
    def set_branch(self, branch, start):
 
118
        return scrollwin
 
119
 
 
120
    def construct_navigation(self):
 
121
        """Construct the navigation buttons."""
 
122
        frame = gtk.Frame()
 
123
        frame.set_shadow_type(gtk.SHADOW_OUT)
 
124
        frame.show()
 
125
        
 
126
        hbox = gtk.HBox(spacing=12)
 
127
        frame.add(hbox)
 
128
        hbox.show()
 
129
 
 
130
        self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
 
131
        self.back_button.set_relief(gtk.RELIEF_NONE)
 
132
        self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
 
133
                                         0, 0)
 
134
        self.back_button.connect("clicked", self._back_clicked_cb)
 
135
        hbox.pack_start(self.back_button, expand=False, fill=True)
 
136
        self.back_button.show()
 
137
 
 
138
        self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
 
139
        self.fwd_button.set_relief(gtk.RELIEF_NONE)
 
140
        self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
 
141
                                        0, 0)
 
142
        self.fwd_button.connect("clicked", self._fwd_clicked_cb)
 
143
        hbox.pack_start(self.fwd_button, expand=False, fill=True)
 
144
        self.fwd_button.show()
 
145
 
 
146
        return frame
 
147
 
 
148
    def construct_bottom(self):
 
149
        """Construct the bottom half of the window."""
 
150
        scrollwin = gtk.ScrolledWindow()
 
151
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
152
        scrollwin.set_shadow_type(gtk.SHADOW_NONE)
 
153
        (width, height) = self.get_size()
 
154
        scrollwin.set_size_request(width, int(height / 2.5))
 
155
        scrollwin.show()
 
156
 
 
157
        vbox = gtk.VBox(False, spacing=6)
 
158
        vbox.set_border_width(6)
 
159
        scrollwin.add_with_viewport(vbox)
 
160
        vbox.show()
 
161
 
 
162
        table = gtk.Table(rows=4, columns=2)
 
163
        table.set_row_spacings(6)
 
164
        table.set_col_spacings(6)
 
165
        vbox.pack_start(table, expand=False, fill=True)
 
166
        table.show()
 
167
 
 
168
        align = gtk.Alignment(0.0, 0.5)
 
169
        label = gtk.Label()
 
170
        label.set_markup("<b>Revision:</b>")
 
171
        align.add(label)
 
172
        table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
173
        label.show()
 
174
        align.show()
 
175
 
 
176
        align = gtk.Alignment(0.0, 0.5)
 
177
        self.revid_label = gtk.Label()
 
178
        self.revid_label.set_selectable(True)
 
179
        align.add(self.revid_label)
 
180
        table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
181
        self.revid_label.show()
 
182
        align.show()
 
183
 
 
184
        align = gtk.Alignment(0.0, 0.5)
 
185
        label = gtk.Label()
 
186
        label.set_markup("<b>Committer:</b>")
 
187
        align.add(label)
 
188
        table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
189
        label.show()
 
190
        align.show()
 
191
 
 
192
        align = gtk.Alignment(0.0, 0.5)
 
193
        self.committer_label = gtk.Label()
 
194
        self.committer_label.set_selectable(True)
 
195
        align.add(self.committer_label)
 
196
        table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
197
        self.committer_label.show()
 
198
        align.show()
 
199
 
 
200
        align = gtk.Alignment(0.0, 0.5)
 
201
        label = gtk.Label()
 
202
        label.set_markup("<b>Branch nick:</b>")
 
203
        align.add(label)
 
204
        table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
 
205
        label.show()
 
206
        align.show()
 
207
 
 
208
        align = gtk.Alignment(0.0, 0.5)
 
209
        self.branchnick_label = gtk.Label()
 
210
        self.branchnick_label.set_selectable(True)
 
211
        align.add(self.branchnick_label)
 
212
        table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
213
        self.branchnick_label.show()
 
214
        align.show()
 
215
 
 
216
        align = gtk.Alignment(0.0, 0.5)
 
217
        label = gtk.Label()
 
218
        label.set_markup("<b>Timestamp:</b>")
 
219
        align.add(label)
 
220
        table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
 
221
        label.show()
 
222
        align.show()
 
223
 
 
224
        align = gtk.Alignment(0.0, 0.5)
 
225
        self.timestamp_label = gtk.Label()
 
226
        self.timestamp_label.set_selectable(True)
 
227
        align.add(self.timestamp_label)
 
228
        table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
229
        self.timestamp_label.show()
 
230
        align.show()
 
231
 
 
232
        self.parents_table = gtk.Table(rows=1, columns=2)
 
233
        self.parents_table.set_row_spacings(3)
 
234
        self.parents_table.set_col_spacings(6)
 
235
        self.parents_table.show()
 
236
        vbox.pack_start(self.parents_table, expand=False, fill=True)
 
237
        self.parents_widgets = []
 
238
 
 
239
        label = gtk.Label()
 
240
        label.set_markup("<b>Parents:</b>")
 
241
        align = gtk.Alignment(0.0, 0.5)
 
242
        align.add(label)
 
243
        self.parents_table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
244
        label.show()
 
245
        align.show()
 
246
 
 
247
        self.message_buffer = gtk.TextBuffer()
 
248
        textview = gtk.TextView(self.message_buffer)
 
249
        textview.set_editable(False)
 
250
        textview.set_wrap_mode(gtk.WRAP_WORD)
 
251
        textview.modify_font(pango.FontDescription("Monospace"))
 
252
        vbox.pack_start(textview, expand=True, fill=True)
 
253
        textview.show()
 
254
 
 
255
        return scrollwin
 
256
 
 
257
    def set_branch(self, branch, start, maxnum):
100
258
        """Set the branch and start position for this window.
101
259
 
102
260
        Creates a new TreeModel and populates it with information about
103
261
        the new branch before updating the window title and model of the
104
262
        treeview itself.
105
263
        """
 
264
        self.branch = branch
 
265
 
106
266
        # [ revision, node, last_lines, lines, message, committer, timestamp ]
107
 
        model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
108
 
                              gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
109
 
                              str, str, str)
 
267
        self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
 
268
                                   gobject.TYPE_PYOBJECT,
 
269
                                   gobject.TYPE_PYOBJECT,
 
270
                                   gobject.TYPE_PYOBJECT,
 
271
                                   str, str, str)
 
272
        self.index = {}
 
273
        index = 0
110
274
 
111
275
        last_lines = []
112
 
        for revision, node, lines in graph(branch, start):
 
276
        (self.revisions, colours, self.children, self.parent_ids,
 
277
         merge_sorted) = distances(branch, start)
 
278
        for (index, (revision, node, lines)) in enumerate(graph(
 
279
                self.revisions, colours, merge_sorted)):
 
280
            # FIXME: at this point we should be able to show the graph order
 
281
            # and lines with no message or commit data - and then incrementally
 
282
            # fill the timestamp, committer etc data as desired.
113
283
            message = revision.message.split("\n")[0]
114
284
            if revision.committer is not None:
115
285
                timestamp = format_date(revision.timestamp, revision.timezone)
116
286
            else:
117
287
                timestamp = None
118
 
 
119
 
            model.append([ revision, node, last_lines, lines,
120
 
                           message, revision.committer, timestamp ])
 
288
            self.model.append([revision, node, last_lines, lines,
 
289
                               message, revision.committer, timestamp])
 
290
            self.index[revision] = index
121
291
            last_lines = lines
122
 
 
123
 
        self.set_title(os.path.basename(branch.base) + " - bzrk")
124
 
        self.treeview.set_model(model)
 
292
            if maxnum is not None and index > maxnum:
 
293
                break
 
294
 
 
295
        self.set_title(branch.nick + " - bzrk")
 
296
        self.treeview.set_model(self.model)
 
297
 
 
298
    def _treeview_cursor_cb(self, *args):
 
299
        """Callback for when the treeview cursor changes."""
 
300
        (path, col) = self.treeview.get_cursor()
 
301
        revision = self.model[path][0]
 
302
 
 
303
        self.back_button.set_sensitive(len(self.parent_ids[revision]) > 0)
 
304
        self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
 
305
 
 
306
        if revision.committer is not None:
 
307
            branchnick = ""
 
308
            committer = revision.committer
 
309
            timestamp = format_date(revision.timestamp, revision.timezone)
 
310
            message = revision.message
 
311
            try:
 
312
                branchnick = revision.properties['branch-nick']
 
313
            except KeyError:
 
314
                pass
 
315
 
 
316
        else:
 
317
            committer = ""
 
318
            timestamp = ""
 
319
            message = ""
 
320
            branchnick = ""
 
321
 
 
322
        self.revid_label.set_text(revision.revision_id)
 
323
        self.branchnick_label.set_text(branchnick)
 
324
 
 
325
        self.committer_label.set_text(committer)
 
326
        self.timestamp_label.set_text(timestamp)
 
327
        self.message_buffer.set_text(message)
 
328
 
 
329
        for widget in self.parents_widgets:
 
330
            self.parents_table.remove(widget)
 
331
 
 
332
        self.parents_widgets = []
 
333
        self.parents_table.resize(max(len(self.parent_ids[revision]), 1), 2)
 
334
        
 
335
        for idx, parent_id in enumerate(self.parent_ids[revision]):
 
336
            align = gtk.Alignment(0.0, 0.0)
 
337
            self.parents_widgets.append(align)
 
338
            self.parents_table.attach(align, 1, 2, idx, idx + 1,
 
339
                                      gtk.EXPAND | gtk.FILL, gtk.FILL)
 
340
            align.show()
 
341
 
 
342
            hbox = gtk.HBox(False, spacing=6)
 
343
            align.add(hbox)
 
344
            hbox.show()
 
345
 
 
346
            image = gtk.Image()
 
347
            image.set_from_stock(
 
348
                gtk.STOCK_FIND, gtk.ICON_SIZE_SMALL_TOOLBAR)
 
349
            image.show()
 
350
 
 
351
            button = gtk.Button()
 
352
            button.add(image)
 
353
            button.set_sensitive(self.app is not None)
 
354
            button.connect("clicked", self._show_clicked_cb,
 
355
                           revision.revision_id, parent_id)
 
356
            hbox.pack_start(button, expand=False, fill=True)
 
357
            button.show()
 
358
 
 
359
            button = gtk.Button(parent_id)
 
360
            button.set_use_underline(False)
 
361
            button.connect("clicked", self._go_clicked_cb, parent_id)
 
362
            hbox.pack_start(button, expand=False, fill=True)
 
363
            button.show()
 
364
 
 
365
 
 
366
    def _back_clicked_cb(self, *args):
 
367
        """Callback for when the back button is clicked."""
 
368
        (path, col) = self.treeview.get_cursor()
 
369
        revision = self.model[path][0]
 
370
        if not len(self.parent_ids[revision]):
 
371
            return
 
372
 
 
373
        for parent_id in self.parent_ids[revision]:
 
374
            parent = self.revisions[parent_id]
 
375
            if same_branch(revision, parent):
 
376
                self.treeview.set_cursor(self.index[parent])
 
377
                break
 
378
        else:
 
379
            next = self.revisions[self.parent_ids[revision][0]]
 
380
            self.treeview.set_cursor(self.index[next])
 
381
        self.treeview.grab_focus()
 
382
 
 
383
    def _fwd_clicked_cb(self, *args):
 
384
        """Callback for when the forward button is clicked."""
 
385
        (path, col) = self.treeview.get_cursor()
 
386
        revision = self.model[path][0]
 
387
        if not len(self.children[revision]):
 
388
            return
 
389
 
 
390
        for child in self.children[revision]:
 
391
            if same_branch(child, revision):
 
392
                self.treeview.set_cursor(self.index[child])
 
393
                break
 
394
        else:
 
395
            prev = list(self.children[revision])[0]
 
396
            self.treeview.set_cursor(self.index[prev])
 
397
        self.treeview.grab_focus()
 
398
 
 
399
    def _go_clicked_cb(self, widget, revid):
 
400
        """Callback for when the go button for a parent is clicked."""
 
401
        self.treeview.set_cursor(self.index[self.revisions[revid]])
 
402
        self.treeview.grab_focus()
 
403
 
 
404
    def _show_clicked_cb(self, widget, revid, parentid):
 
405
        """Callback for when the show button for a parent is clicked."""
 
406
        if self.app is not None:
 
407
            self.app.show_diff(self.branch, revid, parentid)
 
408
        self.treeview.grab_focus()
 
409
 
 
410
    def _treeview_row_activated_cb(self, widget, path, col):
 
411
        # TODO: more than one parent
 
412
        """Callback for when a treeview row gets activated."""
 
413
        revision = self.model[path][0]
 
414
        if len(self.parent_ids[revision]) == 0:
 
415
            # Ignore revisions without parent
 
416
            return
 
417
        parent_id = self.parent_ids[revision][0]
 
418
        if self.app is not None:
 
419
            self.app.show_diff(self.branch, revision.revision_id, parent_id)
 
420
        self.treeview.grab_focus()