/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 revisionview.py

  • Committer: Daniel Schierbeck
  • Date: 2008-01-13 14:12:49 UTC
  • mto: (423.1.2 trunk)
  • mto: This revision was merged to the branch mainline in revision 429.
  • Revision ID: daniel.schierbeck@gmail.com-20080113141249-gd0i2lknr3yik55r
Moved branch view to its own package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005 Dan Loda <danloda@gmail.com>
 
2
# Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org>
2
3
 
3
4
# This program is free software; you can redistribute it and/or modify
4
5
# it under the terms of the GNU General Public License as published by
18
19
pygtk.require("2.0")
19
20
import gtk
20
21
import pango
 
22
import gobject
21
23
 
22
24
from bzrlib.osutils import format_date
23
 
 
24
 
 
25
 
class LogView(gtk.ScrolledWindow):
 
25
from bzrlib.util.bencode import bdecode
 
26
 
 
27
class RevisionView(gtk.Notebook):
26
28
    """ Custom widget for commit log details.
27
29
 
28
30
    A variety of bzr tools may need to implement such a thing. This is a
29
31
    start.
30
32
    """
31
33
 
32
 
    def __init__(self, revision=None):
33
 
        gtk.ScrolledWindow.__init__(self)
34
 
        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
35
 
        self.set_shadow_type(gtk.SHADOW_NONE)
36
 
        self._create()
 
34
    __gproperties__ = {
 
35
        'branch': (
 
36
            gobject.TYPE_PYOBJECT,
 
37
            'Branch',
 
38
            'The branch holding the revision being displayed',
 
39
            gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE
 
40
        ),
 
41
 
 
42
        'revision': (
 
43
            gobject.TYPE_PYOBJECT,
 
44
            'Revision',
 
45
            'The revision being displayed',
 
46
            gobject.PARAM_READWRITE
 
47
        ),
 
48
 
 
49
        'children': (
 
50
            gobject.TYPE_PYOBJECT,
 
51
            'Children',
 
52
            'Child revisions',
 
53
            gobject.PARAM_READWRITE
 
54
        ),
 
55
 
 
56
        'file-id': (
 
57
            gobject.TYPE_PYOBJECT,
 
58
            'File Id',
 
59
            'The file id',
 
60
            gobject.PARAM_READWRITE
 
61
        )
 
62
    }
 
63
 
 
64
 
 
65
    def __init__(self, branch=None):
 
66
        gtk.Notebook.__init__(self)
 
67
 
 
68
        self._create_general()
 
69
        self._create_relations()
 
70
        self._create_file_info_view()
 
71
 
 
72
        self.set_current_page(0)
 
73
        
37
74
        self._show_callback = None
38
 
        self._go_callback = None
39
75
        self._clicked_callback = None
40
76
 
41
 
        if revision is not None:
42
 
            self.set_revision(revision)
 
77
        self._revision = None
 
78
        self._branch = branch
 
79
 
 
80
        if self._branch is not None and self._branch.supports_tags():
 
81
            self._tagdict = self._branch.tags.get_reverse_tag_dict()
 
82
        else:
 
83
            self._tagdict = {}
 
84
 
 
85
        self.set_file_id(None)
 
86
 
 
87
    def do_get_property(self, property):
 
88
        if property.name == 'branch':
 
89
            return self._branch
 
90
        elif property.name == 'revision':
 
91
            return self._revision
 
92
        elif property.name == 'children':
 
93
            return self._children
 
94
        elif property.name == 'file-id':
 
95
            return self._file_id
 
96
        else:
 
97
            raise AttributeError, 'unknown property %s' % property.name
 
98
 
 
99
    def do_set_property(self, property, value):
 
100
        if property.name == 'branch':
 
101
            self._branch = value
 
102
        elif property.name == 'revision':
 
103
            self._set_revision(value)
 
104
        elif property.name == 'children':
 
105
            self.set_children(value)
 
106
        elif property.name == 'file-id':
 
107
            self._file_id = value
 
108
        else:
 
109
            raise AttributeError, 'unknown property %s' % property.name
43
110
 
44
111
    def set_show_callback(self, callback):
45
112
        self._show_callback = callback
46
113
 
47
 
    def set_go_callback(self, callback):
48
 
        self._go_callback = callback
 
114
    def set_file_id(self, file_id):
 
115
        """Set a specific file id that we want to track.
 
116
 
 
117
        This just effects the display of a per-file commit message.
 
118
        If it is set to None, then all commit messages will be shown.
 
119
        """
 
120
        self.set_property('file-id', file_id)
49
121
 
50
122
    def set_revision(self, revision):
 
123
        if revision != self._revision:
 
124
            self.set_property('revision', revision)
 
125
 
 
126
    def get_revision(self):
 
127
        return self.get_property('revision')
 
128
 
 
129
    def _set_revision(self, revision):
 
130
        if revision is None: return
 
131
 
51
132
        self._revision = revision
52
 
        self.revision_id.set_text(revision.revision_id)
53
 
        self.committer.set_text(revision.committer)
54
 
        self.timestamp.set_text(format_date(revision.timestamp,
55
 
                                            revision.timezone))
56
 
        self.message_buffer.set_text(revision.message)
 
133
        if revision.committer is not None:
 
134
            self.committer.set_text(revision.committer)
 
135
        else:
 
136
            self.committer.set_text("")
 
137
        author = revision.properties.get('author', '')
 
138
        if author != '':
 
139
            self.author.set_text(author)
 
140
            self.author.show()
 
141
            self.author_label.show()
 
142
        else:
 
143
            self.author.hide()
 
144
            self.author_label.hide()
 
145
 
 
146
        if revision.timestamp is not None:
 
147
            self.timestamp.set_text(format_date(revision.timestamp,
 
148
                                                revision.timezone))
57
149
        try:
58
150
            self.branchnick_label.set_text(revision.properties['branch-nick'])
59
151
        except KeyError:
60
152
            self.branchnick_label.set_text("")
61
153
 
62
 
        self._add_parents(revision.parent_ids)
 
154
        self._add_parents_or_children(revision.parent_ids,
 
155
                                      self.parents_widgets,
 
156
                                      self.parents_table)
 
157
        
 
158
        file_info = revision.properties.get('file-info', None)
 
159
        if file_info is not None:
 
160
            file_info = bdecode(file_info.encode('UTF-8'))
 
161
 
 
162
        if file_info:
 
163
            if self._file_id is None:
 
164
                text = []
 
165
                for fi in file_info:
 
166
                    text.append('%(path)s\n%(message)s' % fi)
 
167
                self.file_info_buffer.set_text('\n'.join(text))
 
168
                self.file_info_box.show()
 
169
            else:
 
170
                text = []
 
171
                for fi in file_info:
 
172
                    if fi['file_id'] == self._file_id:
 
173
                        text.append(fi['message'])
 
174
                if text:
 
175
                    self.file_info_buffer.set_text('\n'.join(text))
 
176
                    self.file_info_box.show()
 
177
                else:
 
178
                    self.file_info_box.hide()
 
179
        else:
 
180
            self.file_info_box.hide()
 
181
 
 
182
    def set_children(self, children):
 
183
        self._add_parents_or_children(children,
 
184
                                      self.children_widgets,
 
185
                                      self.children_table)
63
186
 
64
187
    def _show_clicked_cb(self, widget, revid, parentid):
65
188
        """Callback for when the show button for a parent is clicked."""
67
190
 
68
191
    def _go_clicked_cb(self, widget, revid):
69
192
        """Callback for when the go button for a parent is clicked."""
70
 
        self._go_callback(revid)
71
193
 
72
 
    def _add_parents(self, parent_ids):
73
 
        for widget in self.parents_widgets:
74
 
            self.parents_table.remove(widget)
 
194
    def _add_tags(self, *args):
 
195
        if self._tagdict.has_key(self._revision.revision_id):
 
196
            tags = self._tagdict[self._revision.revision_id]
 
197
        else:
 
198
            tags = []
75
199
            
76
 
        self.parents_widgets = []
77
 
        self.parents_table.resize(max(len(parent_ids), 1), 2)
78
 
 
79
 
        for idx, parent_id in enumerate(parent_ids):
 
200
        if tags == []:
 
201
            self.tags_list.hide()
 
202
            self.tags_label.hide()
 
203
            return
 
204
 
 
205
        for widget in self.tags_widgets:
 
206
            self.tags_list.remove(widget)
 
207
 
 
208
        self.tags_widgets = []
 
209
 
 
210
        for tag in tags:
 
211
            widget = gtk.Label(tag)
 
212
            widget.set_selectable(True)
 
213
            self.tags_widgets.append(widget)
 
214
            self.tags_list.add(widget)
 
215
        self.tags_list.show_all()
 
216
        self.tags_label.show_all()
 
217
        
 
218
    def _add_parents_or_children(self, revids, widgets, table):
 
219
        while len(widgets) > 0:
 
220
            widget = widgets.pop()
 
221
            table.remove(widget)
 
222
        
 
223
        table.resize(max(len(revids), 1), 2)
 
224
 
 
225
        for idx, revid in enumerate(revids):
80
226
            align = gtk.Alignment(0.0, 0.0)
81
 
            self.parents_widgets.append(align)
82
 
            self.parents_table.attach(align, 1, 2, idx, idx + 1,
 
227
            widgets.append(align)
 
228
            table.attach(align, 1, 2, idx, idx + 1,
83
229
                                      gtk.EXPAND | gtk.FILL, gtk.FILL)
84
230
            align.show()
85
231
 
96
242
                button = gtk.Button()
97
243
                button.add(image)
98
244
                button.connect("clicked", self._show_clicked_cb,
99
 
                               self._revision.revision_id, parent_id)
 
245
                               self._revision.revision_id, revid)
100
246
                hbox.pack_start(button, expand=False, fill=True)
101
247
                button.show()
102
248
 
103
 
            if self._go_callback is not None:
104
 
                button = gtk.Button(parent_id)
105
 
                button.connect("clicked", self._go_clicked_cb, parent_id)
106
 
            else:
107
 
                button = gtk.Label(parent_id)
 
249
            button = gtk.Button(revid)
 
250
            button.connect("clicked",
 
251
                    lambda w, r: self.set_revision(self._branch.repository.get_revision(r)), revid)
108
252
            button.set_use_underline(False)
109
253
            hbox.pack_start(button, expand=False, fill=True)
110
254
            button.show()
111
255
 
112
 
    def _create(self):
 
256
    def _create_general(self):
113
257
        vbox = gtk.VBox(False, 6)
114
258
        vbox.set_border_width(6)
115
259
        vbox.pack_start(self._create_headers(), expand=False, fill=True)
116
 
        vbox.pack_start(self._create_parents_table(), expand=False, fill=True)
117
260
        vbox.pack_start(self._create_message_view())
118
 
        self.add_with_viewport(vbox)
 
261
        self.append_page(vbox, tab_label=gtk.Label("General"))
 
262
        vbox.show()
 
263
 
 
264
    def _create_relations(self):
 
265
        vbox = gtk.VBox(False, 6)
 
266
        vbox.set_border_width(6)
 
267
        vbox.pack_start(self._create_parents(), expand=False, fill=True)
 
268
        vbox.pack_start(self._create_children(), expand=False, fill=True)
 
269
        self.append_page(vbox, tab_label=gtk.Label("Relations"))
119
270
        vbox.show()
120
271
 
121
272
    def _create_headers(self):
122
 
        self.table = gtk.Table(rows=4, columns=2)
 
273
        self.table = gtk.Table(rows=5, columns=2)
123
274
        self.table.set_row_spacings(6)
124
275
        self.table.set_col_spacings(6)
125
276
        self.table.show()
133
284
        label.show()
134
285
 
135
286
        align = gtk.Alignment(0.0, 0.5)
136
 
        self.revision_id = gtk.Label()
137
 
        self.revision_id.set_selectable(True)
138
 
        align.add(self.revision_id)
 
287
        revision_id = gtk.Label()
 
288
        revision_id.set_selectable(True)
 
289
        self.connect('notify::revision', 
 
290
                lambda w, p: revision_id.set_text(self._revision.revision_id))
 
291
        align.add(revision_id)
139
292
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
140
293
        align.show()
141
 
        self.revision_id.show()
 
294
        revision_id.show()
 
295
 
 
296
        align = gtk.Alignment(1.0, 0.5)
 
297
        self.author_label = gtk.Label()
 
298
        self.author_label.set_markup("<b>Author:</b>")
 
299
        align.add(self.author_label)
 
300
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
301
        align.show()
 
302
        self.author_label.show()
 
303
 
 
304
        align = gtk.Alignment(0.0, 0.5)
 
305
        self.author = gtk.Label()
 
306
        self.author.set_selectable(True)
 
307
        align.add(self.author)
 
308
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
309
        align.show()
 
310
        self.author.show()
 
311
        self.author.hide()
142
312
 
143
313
        align = gtk.Alignment(1.0, 0.5)
144
314
        label = gtk.Label()
145
315
        label.set_markup("<b>Committer:</b>")
146
316
        align.add(label)
147
 
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
317
        self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
148
318
        align.show()
149
319
        label.show()
150
320
 
152
322
        self.committer = gtk.Label()
153
323
        self.committer.set_selectable(True)
154
324
        align.add(self.committer)
155
 
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
325
        self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
156
326
        align.show()
157
327
        self.committer.show()
158
328
 
160
330
        label = gtk.Label()
161
331
        label.set_markup("<b>Branch nick:</b>")
162
332
        align.add(label)
163
 
        self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
 
333
        self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
164
334
        label.show()
165
335
        align.show()
166
336
 
168
338
        self.branchnick_label = gtk.Label()
169
339
        self.branchnick_label.set_selectable(True)
170
340
        align.add(self.branchnick_label)
171
 
        self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
341
        self.table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
172
342
        self.branchnick_label.show()
173
343
        align.show()
174
344
 
176
346
        label = gtk.Label()
177
347
        label.set_markup("<b>Timestamp:</b>")
178
348
        align.add(label)
179
 
        self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
 
349
        self.table.attach(align, 0, 1, 4, 5, gtk.FILL, gtk.FILL)
180
350
        align.show()
181
351
        label.show()
182
352
 
184
354
        self.timestamp = gtk.Label()
185
355
        self.timestamp.set_selectable(True)
186
356
        align.add(self.timestamp)
187
 
        self.table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
357
        self.table.attach(align, 1, 2, 4, 5, gtk.EXPAND | gtk.FILL, gtk.FILL)
188
358
        align.show()
189
359
        self.timestamp.show()
190
360
 
 
361
        align = gtk.Alignment(1.0, 0.5)
 
362
        self.tags_label = gtk.Label()
 
363
        self.tags_label.set_markup("<b>Tags:</b>")
 
364
        align.add(self.tags_label)
 
365
        align.show()
 
366
        self.table.attach(align, 0, 1, 5, 6, gtk.FILL, gtk.FILL)
 
367
        self.tags_label.show()
 
368
 
 
369
        align = gtk.Alignment(0.0, 0.5)
 
370
        self.tags_list = gtk.VBox()
 
371
        align.add(self.tags_list)
 
372
        self.table.attach(align, 1, 2, 5, 6, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
373
        align.show()
 
374
        self.tags_list.show()
 
375
        self.tags_widgets = []
 
376
 
 
377
        self.connect('notify::revision', self._add_tags)
 
378
 
191
379
        return self.table
192
380
 
193
 
    def _create_parents_table(self):
194
 
        self.parents_table = gtk.Table(rows=1, columns=2)
195
 
        self.parents_table.set_row_spacings(3)
196
 
        self.parents_table.set_col_spacings(6)
197
 
        self.parents_table.show()
 
381
    
 
382
    def _create_parents(self):
 
383
        hbox = gtk.HBox(True, 3)
 
384
        
 
385
        self.parents_table = self._create_parents_or_children_table(
 
386
            "<b>Parents:</b>")
198
387
        self.parents_widgets = []
 
388
        hbox.pack_start(self.parents_table)
 
389
 
 
390
        hbox.show()
 
391
        return hbox
 
392
 
 
393
    def _create_children(self):
 
394
        hbox = gtk.HBox(True, 3)
 
395
        self.children_table = self._create_parents_or_children_table(
 
396
            "<b>Children:</b>")
 
397
        self.children_widgets = []
 
398
        hbox.pack_start(self.children_table)
 
399
        hbox.show()
 
400
        return hbox
 
401
        
 
402
    def _create_parents_or_children_table(self, text):
 
403
        table = gtk.Table(rows=1, columns=2)
 
404
        table.set_row_spacings(3)
 
405
        table.set_col_spacings(6)
 
406
        table.show()
199
407
 
200
408
        label = gtk.Label()
201
 
        label.set_markup("<b>Parents:</b>")
 
409
        label.set_markup(text)
202
410
        align = gtk.Alignment(0.0, 0.5)
203
411
        align.add(label)
204
 
        self.parents_table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
412
        table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
205
413
        label.show()
206
414
        align.show()
207
415
 
208
 
        return self.parents_table
 
416
        return table
209
417
 
210
418
    def _create_message_view(self):
211
 
        self.message_buffer = gtk.TextBuffer()
212
 
        tv = gtk.TextView(self.message_buffer)
213
 
        tv.set_editable(False)
214
 
        tv.set_wrap_mode(gtk.WRAP_WORD)
215
 
        tv.modify_font(pango.FontDescription("Monospace"))
216
 
        tv.show()
217
 
        return tv
 
419
        msg_buffer = gtk.TextBuffer()
 
420
        self.connect('notify::revision',
 
421
                lambda w, p: msg_buffer.set_text(self._revision.message))
 
422
        window = gtk.ScrolledWindow()
 
423
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
424
        window.set_shadow_type(gtk.SHADOW_IN)
 
425
        tv = gtk.TextView(msg_buffer)
 
426
        tv.set_editable(False)
 
427
        tv.set_wrap_mode(gtk.WRAP_WORD)
 
428
        tv.modify_font(pango.FontDescription("Monospace"))
 
429
        tv.show()
 
430
        window.add(tv)
 
431
        window.show()
 
432
        return window
 
433
 
 
434
    def _create_file_info_view(self):
 
435
        self.file_info_box = gtk.VBox(False, 6)
 
436
        self.file_info_box.set_border_width(6)
 
437
        self.file_info_buffer = gtk.TextBuffer()
 
438
        window = gtk.ScrolledWindow()
 
439
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
440
        window.set_shadow_type(gtk.SHADOW_IN)
 
441
        tv = gtk.TextView(self.file_info_buffer)
 
442
        tv.set_editable(False)
 
443
        tv.set_wrap_mode(gtk.WRAP_WORD)
 
444
        tv.modify_font(pango.FontDescription("Monospace"))
 
445
        tv.show()
 
446
        window.add(tv)
 
447
        window.show()
 
448
        self.file_info_box.pack_start(window)
 
449
        self.file_info_box.hide() # Only shown when there are per-file messages
 
450
        self.append_page(self.file_info_box, tab_label=gtk.Label('Per-file'))
218
451