/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: Andrew Bennetts
  • Date: 2008-04-29 08:17:01 UTC
  • mto: This revision was merged to the branch mainline in revision 476.
  • Revision ID: andrew@puzzling.org-20080429081701-2pu9uteic8o88nre
Simple hack to fix gannotate.

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