/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: Martin Albisetti
  • Date: 2008-04-04 01:46:01 UTC
  • Revision ID: argentina@gmail.com-20080404014601-hutl46uqxx7psg2o
Removed duplicate code

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
 
23
import subprocess
21
24
 
 
25
from bzrlib.plugins.gtk import icon_path
22
26
from bzrlib.osutils import format_date
23
 
 
24
 
 
25
 
class LogView(gtk.ScrolledWindow):
 
27
from bzrlib.util.bencode import bdecode
 
28
 
 
29
try:
 
30
    from bzrlib.plugins.gtk import seahorse
 
31
except ImportError:
 
32
    has_seahorse = False
 
33
else:
 
34
    has_seahorse = True
 
35
 
 
36
def _open_link(widget, uri):
 
37
    subprocess.Popen(['sensible-browser', uri], close_fds=True)
 
38
 
 
39
gtk.link_button_set_uri_hook(_open_link)
 
40
 
 
41
class BugsTab(gtk.Table):
 
42
 
 
43
    def __init__(self):
 
44
        super(BugsTab, self).__init__(rows=5, columns=2)
 
45
        self.set_row_spacings(6)
 
46
        self.set_col_spacings(6)
 
47
        self.clear()
 
48
 
 
49
    def clear(self):
 
50
        for c in self.get_children():
 
51
            self.remove(c)
 
52
        self.count = 0
 
53
        self.hide_all() # Only shown when there are bugs
 
54
 
 
55
    def add_bug(self, url, status):
 
56
        button = gtk.LinkButton(url, url)
 
57
        self.attach(button, 0, 1, self.count, self.count + 1,
 
58
                              gtk.EXPAND | gtk.FILL, gtk.FILL)
 
59
        status_label = gtk.Label(status)
 
60
        self.attach(status_label, 1, 2, self.count, self.count + 1,
 
61
                              gtk.EXPAND | gtk.FILL, gtk.FILL)
 
62
        self.count += 1
 
63
        self.show_all()
 
64
 
 
65
 
 
66
class SignatureTab(gtk.VBox):
 
67
 
 
68
    def __init__(self, repository):
 
69
        self.key = None
 
70
        self.revision = None
 
71
        self.repository = repository
 
72
 
 
73
        super(SignatureTab, self).__init__(False, 6)
 
74
        signature_box = gtk.Table(rows=3, columns=3)
 
75
        signature_box.set_col_spacing(0, 16)
 
76
        signature_box.set_col_spacing(1, 12)
 
77
        signature_box.set_row_spacings(6)
 
78
 
 
79
        self.signature_image = gtk.Image()
 
80
        signature_box.attach(self.signature_image, 0, 1, 0, 1, gtk.FILL)
 
81
 
 
82
        align = gtk.Alignment(0.0, 0.1)
 
83
        self.signature_label = gtk.Label()
 
84
        align.add(self.signature_label)
 
85
        signature_box.attach(align, 1, 3, 0, 1, gtk.FILL)
 
86
 
 
87
        align = gtk.Alignment(0.0, 0.5)
 
88
        self.signature_key_id_label = gtk.Label()
 
89
        self.signature_key_id_label.set_markup("<b>Key Id:</b>")
 
90
        align.add(self.signature_key_id_label)
 
91
        signature_box.attach(align, 1, 2, 1, 2, gtk.FILL, gtk.FILL)
 
92
 
 
93
        align = gtk.Alignment(0.0, 0.5)
 
94
        self.signature_key_id = gtk.Label()
 
95
        self.signature_key_id.set_selectable(True)
 
96
        align.add(self.signature_key_id)
 
97
        signature_box.attach(align, 2, 3, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
98
 
 
99
        align = gtk.Alignment(0.0, 0.5)
 
100
        self.signature_fingerprint_label = gtk.Label()
 
101
        self.signature_fingerprint_label.set_markup("<b>Fingerprint:</b>")
 
102
        align.add(self.signature_fingerprint_label)
 
103
        signature_box.attach(align, 1, 2, 2, 3, gtk.FILL, gtk.FILL)
 
104
 
 
105
        align = gtk.Alignment(0.0, 0.5)
 
106
        self.signature_fingerprint = gtk.Label()
 
107
        self.signature_fingerprint.set_selectable(True)
 
108
        align.add(self.signature_fingerprint)
 
109
        signature_box.attach(align, 2, 3, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
110
 
 
111
        align = gtk.Alignment(0.0, 0.5)
 
112
        self.signature_trust_label = gtk.Label()
 
113
        self.signature_trust_label.set_markup("<b>Trust:</b>")
 
114
        align.add(self.signature_trust_label)
 
115
        signature_box.attach(align, 1, 2, 3, 4, gtk.FILL, gtk.FILL)
 
116
 
 
117
        align = gtk.Alignment(0.0, 0.5)
 
118
        self.signature_trust = gtk.Label()
 
119
        self.signature_trust.set_selectable(True)
 
120
        align.add(self.signature_trust)
 
121
        signature_box.attach(align, 2, 3, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
122
 
 
123
        self.set_border_width(6)
 
124
        self.pack_start(signature_box, expand=False)
 
125
        self.show_all()
 
126
 
 
127
    def set_revision(self, revision):
 
128
        self.revision = revision
 
129
        revid = revision.revision_id
 
130
 
 
131
        if self.repository.has_signature_for_revision_id(revid):
 
132
            crypttext = self.repository.get_signature_text(revid)
 
133
            self.show_signature(crypttext)
 
134
        else:
 
135
            self.show_no_signature()
 
136
 
 
137
    def show_no_signature(self):
 
138
        self.signature_key_id_label.hide()
 
139
        self.signature_key_id.set_text("")
 
140
 
 
141
        self.signature_fingerprint_label.hide()
 
142
        self.signature_fingerprint.set_text("")
 
143
 
 
144
        self.signature_trust_label.hide()
 
145
        self.signature_trust.set_text("")
 
146
 
 
147
        self.signature_image.set_from_file(icon_path("sign-unknown.png"))
 
148
        self.signature_label.set_markup("<b>Authenticity unknown</b>\n" +
 
149
                                        "This revision has not been signed.")
 
150
 
 
151
    def show_signature(self, crypttext):
 
152
        key = seahorse.verify(crypttext)
 
153
 
 
154
        if key and key.is_available():
 
155
            if key.is_trusted():
 
156
                if key.get_display_name() == self.revision.committer:
 
157
                    self.signature_image.set_from_file(icon_path("sign-ok.png"))
 
158
                    self.signature_label.set_markup("<b>Authenticity confirmed</b>\n" +
 
159
                                                    "This revision has been signed with " +
 
160
                                                    "a trusted key.")
 
161
                else:
 
162
                    self.signature_image.set_from_file(icon_path("sign-bad.png"))
 
163
                    self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
 
164
                                                    "Revision committer is not the same as signer.")
 
165
            else:
 
166
                self.signature_image.set_from_file(icon_path("sign-bad.png"))
 
167
                self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
 
168
                                                "This revision has been signed, but the " +
 
169
                                                "key is not trusted.")
 
170
        else:
 
171
            self.show_no_signature()
 
172
            self.signature_image.set_from_file(icon_path("sign-bad.png"))
 
173
            self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
 
174
                                            "Signature key not available.")
 
175
            return
 
176
 
 
177
        trust = key.get_trust()
 
178
 
 
179
        if trust <= seahorse.TRUST_NEVER:
 
180
            trust_text = 'never trusted'
 
181
        elif trust == seahorse.TRUST_UNKNOWN:
 
182
            trust_text = 'not trusted'
 
183
        elif trust == seahorse.TRUST_MARGINAL:
 
184
            trust_text = 'marginally trusted'
 
185
        elif trust == seahorse.TRUST_FULL:
 
186
            trust_text = 'fully trusted'
 
187
        elif trust == seahorse.TRUST_ULTIMATE:
 
188
            trust_text = 'ultimately trusted'
 
189
 
 
190
        self.signature_key_id_label.show()
 
191
        self.signature_key_id.set_text(key.get_id())
 
192
 
 
193
        fingerprint = key.get_fingerprint()
 
194
        if fingerprint == "":
 
195
            fingerprint = '<span foreground="dim grey">N/A</span>'
 
196
 
 
197
        self.signature_fingerprint_label.show()
 
198
        self.signature_fingerprint.set_markup(fingerprint)
 
199
 
 
200
        self.signature_trust_label.show()
 
201
        self.signature_trust.set_text('This key is ' + trust_text)
 
202
 
 
203
 
 
204
class RevisionView(gtk.Notebook):
26
205
    """ Custom widget for commit log details.
27
206
 
28
207
    A variety of bzr tools may need to implement such a thing. This is a
29
208
    start.
30
209
    """
31
210
 
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)
 
211
    __gproperties__ = {
 
212
        'branch': (
 
213
            gobject.TYPE_PYOBJECT,
 
214
            'Branch',
 
215
            'The branch holding the revision being displayed',
 
216
            gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE
 
217
        ),
 
218
 
 
219
        'revision': (
 
220
            gobject.TYPE_PYOBJECT,
 
221
            'Revision',
 
222
            'The revision being displayed',
 
223
            gobject.PARAM_READWRITE
 
224
        ),
 
225
 
 
226
        'children': (
 
227
            gobject.TYPE_PYOBJECT,
 
228
            'Children',
 
229
            'Child revisions',
 
230
            gobject.PARAM_READWRITE
 
231
        ),
 
232
 
 
233
        'file-id': (
 
234
            gobject.TYPE_PYOBJECT,
 
235
            'File Id',
 
236
            'The file id',
 
237
            gobject.PARAM_READWRITE
 
238
        )
 
239
    }
 
240
 
 
241
 
 
242
    def __init__(self, branch=None):
 
243
        gtk.Notebook.__init__(self)
 
244
 
 
245
        self._revision = None
 
246
        self._branch = branch
 
247
 
 
248
        self._create_general()
 
249
        self._create_relations()
 
250
        if has_seahorse:
 
251
            self._create_signature()
 
252
        self._create_file_info_view()
 
253
        self._create_bugs()
 
254
 
 
255
        self.set_current_page(0)
 
256
        
 
257
        self._show_callback = None
 
258
        self._clicked_callback = None
 
259
 
 
260
        self._revision = None
 
261
        self._branch = branch
 
262
 
 
263
        self.update_tags()
 
264
 
 
265
        self.set_file_id(None)
 
266
 
 
267
    def do_get_property(self, property):
 
268
        if property.name == 'branch':
 
269
            return self._branch
 
270
        elif property.name == 'revision':
 
271
            return self._revision
 
272
        elif property.name == 'children':
 
273
            return self._children
 
274
        elif property.name == 'file-id':
 
275
            return self._file_id
 
276
        else:
 
277
            raise AttributeError, 'unknown property %s' % property.name
 
278
 
 
279
    def do_set_property(self, property, value):
 
280
        if property.name == 'branch':
 
281
            self._branch = value
 
282
        elif property.name == 'revision':
 
283
            self._set_revision(value)
 
284
        elif property.name == 'children':
 
285
            self.set_children(value)
 
286
        elif property.name == 'file-id':
 
287
            self._file_id = value
 
288
        else:
 
289
            raise AttributeError, 'unknown property %s' % property.name
 
290
 
 
291
    def set_show_callback(self, callback):
 
292
        self._show_callback = callback
 
293
 
 
294
    def set_file_id(self, file_id):
 
295
        """Set a specific file id that we want to track.
 
296
 
 
297
        This just effects the display of a per-file commit message.
 
298
        If it is set to None, then all commit messages will be shown.
 
299
        """
 
300
        self.set_property('file-id', file_id)
41
301
 
42
302
    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)
 
303
        if revision != self._revision:
 
304
            self.set_property('revision', revision)
 
305
 
 
306
    def get_revision(self):
 
307
        return self.get_property('revision')
 
308
 
 
309
    def _set_revision(self, revision):
 
310
        if revision is None: return
 
311
 
 
312
        self._revision = revision
 
313
        if revision.committer is not None:
 
314
            self.committer.set_text(revision.committer)
 
315
        else:
 
316
            self.committer.set_text("")
 
317
        author = revision.properties.get('author', '')
 
318
        if author != '':
 
319
            self.author.set_text(author)
 
320
            self.author.show()
 
321
            self.author_label.show()
 
322
        else:
 
323
            self.author.hide()
 
324
            self.author_label.hide()
 
325
 
 
326
        if revision.timestamp is not None:
 
327
            self.timestamp.set_text(format_date(revision.timestamp,
 
328
                                                revision.timezone))
 
329
        try:
 
330
            self.branchnick_label.set_text(revision.properties['branch-nick'])
 
331
        except KeyError:
 
332
            self.branchnick_label.set_text("")
 
333
 
 
334
        self._add_parents_or_children(revision.parent_ids,
 
335
                                      self.parents_widgets,
 
336
                                      self.parents_table)
 
337
        
 
338
        file_info = revision.properties.get('file-info', None)
 
339
        if file_info is not None:
 
340
            file_info = bdecode(file_info.encode('UTF-8'))
 
341
 
 
342
        if file_info:
 
343
            if self._file_id is None:
 
344
                text = []
 
345
                for fi in file_info:
 
346
                    text.append('%(path)s\n%(message)s' % fi)
 
347
                self.file_info_buffer.set_text('\n'.join(text))
 
348
                self.file_info_box.show()
 
349
            else:
 
350
                text = []
 
351
                for fi in file_info:
 
352
                    if fi['file_id'] == self._file_id:
 
353
                        text.append(fi['message'])
 
354
                if text:
 
355
                    self.file_info_buffer.set_text('\n'.join(text))
 
356
                    self.file_info_box.show()
 
357
                else:
 
358
                    self.file_info_box.hide()
 
359
        else:
 
360
            self.file_info_box.hide()
 
361
 
 
362
        self.bugs_table.clear()
 
363
        bugs_text = revision.properties.get('bugs', None)
 
364
        if bugs_text:
 
365
            for bugline in bugs_text.splitlines():
 
366
                (url, status) = bugline.split(" ")
 
367
                self.bugs_table.add_bug(url, status)
 
368
 
 
369
    def update_tags(self):
 
370
        if self._branch is not None and self._branch.supports_tags():
 
371
            self._tagdict = self._branch.tags.get_reverse_tag_dict()
 
372
        else:
 
373
            self._tagdict = {}
 
374
 
 
375
        self._add_tags()
 
376
 
 
377
    def _update_signature(self, widget, param):
 
378
        self.signature_table.set_revision(self._revision)
 
379
 
 
380
    def set_children(self, children):
 
381
        self._add_parents_or_children(children,
 
382
                                      self.children_widgets,
 
383
                                      self.children_table)
 
384
 
 
385
    def _show_clicked_cb(self, widget, revid, parentid):
 
386
        """Callback for when the show button for a parent is clicked."""
 
387
        self._show_callback(revid, parentid)
 
388
 
 
389
    def _go_clicked_cb(self, widget, revid):
 
390
        """Callback for when the go button for a parent is clicked."""
 
391
 
 
392
    def _add_tags(self, *args):
 
393
        if self._revision is None:
 
394
            return
 
395
 
 
396
        if self._tagdict.has_key(self._revision.revision_id):
 
397
            tags = self._tagdict[self._revision.revision_id]
 
398
        else:
 
399
            tags = []
53
400
            
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)
 
401
        if tags == []:
 
402
            self.tags_list.hide()
 
403
            self.tags_label.hide()
 
404
            return
 
405
 
 
406
        self.tags_list.set_text(", ".join(tags))
 
407
 
 
408
        self.tags_list.show_all()
 
409
        self.tags_label.show_all()
 
410
        
 
411
    def _add_parents_or_children(self, revids, widgets, table):
 
412
        while len(widgets) > 0:
 
413
            widget = widgets.pop()
 
414
            table.remove(widget)
 
415
        
 
416
        table.resize(max(len(revids), 1), 2)
 
417
 
 
418
        for idx, revid in enumerate(revids):
 
419
            align = gtk.Alignment(0.0, 0.0)
 
420
            widgets.append(align)
 
421
            table.attach(align, 1, 2, idx, idx + 1,
 
422
                                      gtk.EXPAND | gtk.FILL, gtk.FILL)
60
423
            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):
 
424
 
 
425
            hbox = gtk.HBox(False, spacing=6)
 
426
            align.add(hbox)
 
427
            hbox.show()
 
428
 
 
429
            image = gtk.Image()
 
430
            image.set_from_stock(
 
431
                gtk.STOCK_FIND, gtk.ICON_SIZE_SMALL_TOOLBAR)
 
432
            image.show()
 
433
 
 
434
            if self._show_callback is not None:
 
435
                button = gtk.Button()
 
436
                button.add(image)
 
437
                button.connect("clicked", self._show_clicked_cb,
 
438
                               self._revision.revision_id, revid)
 
439
                hbox.pack_start(button, expand=False, fill=True)
 
440
                button.show()
 
441
 
 
442
            button = gtk.Button(revid)
 
443
            button.connect("clicked",
 
444
                    lambda w, r: self.set_revision(self._branch.repository.get_revision(r)), revid)
 
445
            button.set_use_underline(False)
 
446
            hbox.pack_start(button, expand=False, fill=True)
 
447
            button.show()
 
448
 
 
449
    def _create_general(self):
84
450
        vbox = gtk.VBox(False, 6)
85
451
        vbox.set_border_width(6)
86
452
        vbox.pack_start(self._create_headers(), expand=False, fill=True)
87
453
        vbox.pack_start(self._create_message_view())
88
 
        self.add_with_viewport(vbox)
89
 
        vbox.show()
 
454
        self.append_page(vbox, tab_label=gtk.Label("General"))
 
455
        vbox.show()
 
456
 
 
457
    def _create_relations(self):
 
458
        vbox = gtk.VBox(False, 6)
 
459
        vbox.set_border_width(6)
 
460
        vbox.pack_start(self._create_parents(), expand=False, fill=True)
 
461
        vbox.pack_start(self._create_children(), expand=False, fill=True)
 
462
        self.append_page(vbox, tab_label=gtk.Label("Relations"))
 
463
        vbox.show()
 
464
 
 
465
    def _create_signature(self):
 
466
        self.signature_table = SignatureTab(self._branch.repository)
 
467
        self.append_page(self.signature_table, tab_label=gtk.Label('Signature'))
 
468
        self.connect_after('notify::revision', self._update_signature)
90
469
 
91
470
    def _create_headers(self):
92
 
        self.table = gtk.Table(rows=4, columns=2)
 
471
        self.table = gtk.Table(rows=5, columns=2)
93
472
        self.table.set_row_spacings(6)
94
473
        self.table.set_col_spacings(6)
95
474
        self.table.show()
96
 
        
 
475
 
 
476
        align = gtk.Alignment(1.0, 0.5)
 
477
        label = gtk.Label()
 
478
        label.set_markup("<b>Revision Id:</b>")
 
479
        align.add(label)
 
480
        self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
481
        align.show()
 
482
        label.show()
 
483
 
 
484
        align = gtk.Alignment(0.0, 0.5)
 
485
        revision_id = gtk.Label()
 
486
        revision_id.set_selectable(True)
 
487
        self.connect('notify::revision', 
 
488
                lambda w, p: revision_id.set_text(self._revision.revision_id))
 
489
        align.add(revision_id)
 
490
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
491
        align.show()
 
492
        revision_id.show()
 
493
 
 
494
        align = gtk.Alignment(1.0, 0.5)
 
495
        self.author_label = gtk.Label()
 
496
        self.author_label.set_markup("<b>Author:</b>")
 
497
        align.add(self.author_label)
 
498
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
499
        align.show()
 
500
        self.author_label.show()
 
501
 
 
502
        align = gtk.Alignment(0.0, 0.5)
 
503
        self.author = gtk.Label()
 
504
        self.author.set_selectable(True)
 
505
        align.add(self.author)
 
506
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
507
        align.show()
 
508
        self.author.show()
 
509
        self.author.hide()
 
510
 
97
511
        align = gtk.Alignment(1.0, 0.5)
98
512
        label = gtk.Label()
99
513
        label.set_markup("<b>Committer:</b>")
100
514
        align.add(label)
101
 
        self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
515
        self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
102
516
        align.show()
103
517
        label.show()
104
518
 
106
520
        self.committer = gtk.Label()
107
521
        self.committer.set_selectable(True)
108
522
        align.add(self.committer)
109
 
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
523
        self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
110
524
        align.show()
111
525
        self.committer.show()
112
526
 
 
527
        align = gtk.Alignment(0.0, 0.5)
 
528
        label = gtk.Label()
 
529
        label.set_markup("<b>Branch nick:</b>")
 
530
        align.add(label)
 
531
        self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
 
532
        label.show()
 
533
        align.show()
 
534
 
 
535
        align = gtk.Alignment(0.0, 0.5)
 
536
        self.branchnick_label = gtk.Label()
 
537
        self.branchnick_label.set_selectable(True)
 
538
        align.add(self.branchnick_label)
 
539
        self.table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
540
        self.branchnick_label.show()
 
541
        align.show()
 
542
 
113
543
        align = gtk.Alignment(1.0, 0.5)
114
544
        label = gtk.Label()
115
545
        label.set_markup("<b>Timestamp:</b>")
116
546
        align.add(label)
117
 
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
547
        self.table.attach(align, 0, 1, 4, 5, gtk.FILL, gtk.FILL)
118
548
        align.show()
119
549
        label.show()
120
550
 
122
552
        self.timestamp = gtk.Label()
123
553
        self.timestamp.set_selectable(True)
124
554
        align.add(self.timestamp)
125
 
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
555
        self.table.attach(align, 1, 2, 4, 5, gtk.EXPAND | gtk.FILL, gtk.FILL)
126
556
        align.show()
127
557
        self.timestamp.show()
128
558
 
129
559
        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)
 
560
        self.tags_label = gtk.Label()
 
561
        self.tags_label.set_markup("<b>Tags:</b>")
 
562
        align.add(self.tags_label)
134
563
        align.show()
135
 
        label.show()
 
564
        self.table.attach(align, 0, 1, 5, 6, gtk.FILL, gtk.FILL)
 
565
        self.tags_label.show()
136
566
 
137
567
        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)
 
568
        self.tags_list = gtk.Label()
 
569
        align.add(self.tags_list)
 
570
        self.table.attach(align, 1, 2, 5, 6, gtk.EXPAND | gtk.FILL, gtk.FILL)
142
571
        align.show()
143
 
        self.revision_id.show()
 
572
        self.tags_list.show()
 
573
 
 
574
        self.connect('notify::revision', self._add_tags)
144
575
 
145
576
        return self.table
 
577
    
 
578
    def _create_parents(self):
 
579
        hbox = gtk.HBox(True, 3)
 
580
        
 
581
        self.parents_table = self._create_parents_or_children_table(
 
582
            "<b>Parents:</b>")
 
583
        self.parents_widgets = []
 
584
        hbox.pack_start(self.parents_table)
 
585
 
 
586
        hbox.show()
 
587
        return hbox
 
588
 
 
589
    def _create_children(self):
 
590
        hbox = gtk.HBox(True, 3)
 
591
        self.children_table = self._create_parents_or_children_table(
 
592
            "<b>Children:</b>")
 
593
        self.children_widgets = []
 
594
        hbox.pack_start(self.children_table)
 
595
        hbox.show()
 
596
        return hbox
 
597
        
 
598
    def _create_parents_or_children_table(self, text):
 
599
        table = gtk.Table(rows=1, columns=2)
 
600
        table.set_row_spacings(3)
 
601
        table.set_col_spacings(6)
 
602
        table.show()
 
603
 
 
604
        label = gtk.Label()
 
605
        label.set_markup(text)
 
606
        align = gtk.Alignment(0.0, 0.5)
 
607
        align.add(label)
 
608
        table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
609
        label.show()
 
610
        align.show()
 
611
 
 
612
        return table
146
613
 
147
614
    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
 
615
        msg_buffer = gtk.TextBuffer()
 
616
        self.connect('notify::revision',
 
617
                lambda w, p: msg_buffer.set_text(self._revision.message))
 
618
        window = gtk.ScrolledWindow()
 
619
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
620
        window.set_shadow_type(gtk.SHADOW_IN)
 
621
        tv = gtk.TextView(msg_buffer)
 
622
        tv.set_editable(False)
 
623
        tv.set_wrap_mode(gtk.WRAP_WORD)
 
624
 
 
625
        tv.modify_font(pango.FontDescription("Monospace"))
 
626
        tv.show()
 
627
        window.add(tv)
 
628
        window.show()
 
629
        return window
 
630
 
 
631
    def _create_bugs(self):
 
632
        self.bugs_table = BugsTab()
 
633
        self.append_page(self.bugs_table, tab_label=gtk.Label('Bugs'))
 
634
 
 
635
    def _create_file_info_view(self):
 
636
        self.file_info_box = gtk.VBox(False, 6)
 
637
        self.file_info_box.set_border_width(6)
 
638
        self.file_info_buffer = gtk.TextBuffer()
 
639
        window = gtk.ScrolledWindow()
 
640
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
641
        window.set_shadow_type(gtk.SHADOW_IN)
 
642
        tv = gtk.TextView(self.file_info_buffer)
 
643
        tv.set_editable(False)
 
644
        tv.set_wrap_mode(gtk.WRAP_WORD)
 
645
        tv.modify_font(pango.FontDescription("Monospace"))
 
646
        tv.show()
 
647
        window.add(tv)
 
648
        window.show()
 
649
        self.file_info_box.pack_start(window)
 
650
        self.file_info_box.hide() # Only shown when there are per-file messages
 
651
        self.append_page(self.file_info_box, tab_label=gtk.Label('Per-file'))
155
652