/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: Jelmer Vernooij
  • Date: 2008-03-09 13:47:52 UTC
  • mto: This revision was merged to the branch mainline in revision 447.
  • Revision ID: jelmer@samba.org-20080309134752-syf9kwzy6e919jhj
Add note about python-nautilus requiring a libpythonXX.so symlink.

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