/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: 2006-09-05 02:17:19 UTC
  • Revision ID: jelmer@samba.org-20060905021719-90e2eda34c41604b
Handle empty files more gracefully. Fixes #58951.

Show diffs side-by-side

added added

removed removed

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