/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-06-29 16:11:12 UTC
  • mfrom: (475.2.2 gtk)
  • mto: This revision was merged to the branch mainline in revision 519.
  • Revision ID: jelmer@samba.org-20080629161112-3j4zp0r0e7cv6cds
Merge Chad's progress bar in viz patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
pygtk.require("2.0")
20
20
import gtk
21
21
import pango
22
 
import gtksourceview
23
 
import sys
24
 
 
25
 
from cStringIO import StringIO
26
 
 
 
22
import gobject
 
23
import webbrowser
 
24
 
 
25
from bzrlib.plugins.gtk import icon_path
27
26
from bzrlib.osutils import format_date
28
 
from bzrlib.diff import show_diff_trees
29
 
 
30
 
class LogView(gtk.Notebook):
 
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
 
 
42
def _open_link(widget, uri):
 
43
    for cmd in ['sensible-browser', 'xdg-open']:
 
44
        if webbrowser._iscommand(cmd):
 
45
            webbrowser._tryorder.insert(0, '%s "%%s"' % cmd)
 
46
    webbrowser.open(uri)
 
47
 
 
48
gtk.link_button_set_uri_hook(_open_link)
 
49
 
 
50
class BugsTab(gtk.VBox):
 
51
 
 
52
    def __init__(self):
 
53
        super(BugsTab, self).__init__(False, 6)
 
54
    
 
55
        table = gtk.Table(rows=2, columns=2)
 
56
 
 
57
        table.set_row_spacings(6)
 
58
        table.set_col_spacing(0, 16)
 
59
 
 
60
        image = gtk.Image()
 
61
        image.set_from_file(icon_path("bug.png"))
 
62
        table.attach(image, 0, 1, 0, 1, gtk.FILL)
 
63
 
 
64
        align = gtk.Alignment(0.0, 0.1)
 
65
        self.label = gtk.Label()
 
66
        align.add(self.label)
 
67
        table.attach(align, 1, 2, 0, 1, gtk.FILL)
 
68
 
 
69
        treeview = self.construct_treeview()
 
70
        table.attach(treeview, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND)
 
71
 
 
72
        self.set_border_width(6)
 
73
        self.pack_start(table, expand=False)
 
74
 
 
75
        self.clear()
 
76
        self.show_all()
 
77
 
 
78
    def set_revision(self, revision):
 
79
        if revision is None:
 
80
            return
 
81
 
 
82
        self.clear()
 
83
        bugs_text = revision.properties.get('bugs', '')
 
84
        for bugline in bugs_text.splitlines():
 
85
                (url, status) = bugline.split(" ")
 
86
                if status == "fixed":
 
87
                    self.add_bug(url, status)
 
88
        
 
89
        if self.num_bugs == 0:
 
90
            return
 
91
        elif self.num_bugs == 1:
 
92
            label = "bug"
 
93
        else:
 
94
            label = "bugs"
 
95
 
 
96
        self.label.set_markup("<b>Bugs fixed</b>\n" +
 
97
                              "This revision claims to fix " +
 
98
                              "%d %s." % (self.num_bugs, label))
 
99
 
 
100
    def construct_treeview(self):
 
101
        self.bugs = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
 
102
        self.treeview = gtk.TreeView(self.bugs)
 
103
        self.treeview.set_headers_visible(False)
 
104
 
 
105
        uri_column = gtk.TreeViewColumn('Bug URI', gtk.CellRendererText(), text=0)
 
106
        self.treeview.append_column(uri_column)
 
107
 
 
108
        self.treeview.connect('row-activated', self.on_row_activated)
 
109
 
 
110
        win = gtk.ScrolledWindow()
 
111
        win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
112
        win.set_shadow_type(gtk.SHADOW_IN)
 
113
        win.add(self.treeview)
 
114
 
 
115
        return win
 
116
 
 
117
    def clear(self):
 
118
        self.num_bugs = 0
 
119
        self.bugs.clear()
 
120
        self.set_sensitive(False)
 
121
        self.label.set_markup("<b>No bugs fixed</b>\n" +
 
122
                              "This revision does not claim to fix any bugs.")
 
123
 
 
124
    def add_bug(self, url, status):
 
125
        self.num_bugs += 1
 
126
        self.bugs.append([url, status])
 
127
        self.set_sensitive(True)
 
128
 
 
129
    def get_num_bugs(self):
 
130
        return self.num_bugs
 
131
 
 
132
    def on_row_activated(self, treeview, path, column):
 
133
        uri = self.bugs.get_value(self.bugs.get_iter(path), 0)
 
134
        _open_link(self, uri)
 
135
 
 
136
 
 
137
class SignatureTab(gtk.VBox):
 
138
 
 
139
    def __init__(self, repository):
 
140
        self.key = None
 
141
        self.revision = None
 
142
        self.repository = repository
 
143
 
 
144
        super(SignatureTab, self).__init__(False, 6)
 
145
        signature_box = gtk.Table(rows=3, columns=3)
 
146
        signature_box.set_col_spacing(0, 16)
 
147
        signature_box.set_col_spacing(1, 12)
 
148
        signature_box.set_row_spacings(6)
 
149
 
 
150
        self.signature_image = gtk.Image()
 
151
        signature_box.attach(self.signature_image, 0, 1, 0, 1, gtk.FILL)
 
152
 
 
153
        align = gtk.Alignment(0.0, 0.1)
 
154
        self.signature_label = gtk.Label()
 
155
        align.add(self.signature_label)
 
156
        signature_box.attach(align, 1, 3, 0, 1, gtk.FILL)
 
157
 
 
158
        align = gtk.Alignment(0.0, 0.5)
 
159
        self.signature_key_id_label = gtk.Label()
 
160
        self.signature_key_id_label.set_markup("<b>Key Id:</b>")
 
161
        align.add(self.signature_key_id_label)
 
162
        signature_box.attach(align, 1, 2, 1, 2, gtk.FILL, gtk.FILL)
 
163
 
 
164
        align = gtk.Alignment(0.0, 0.5)
 
165
        self.signature_key_id = gtk.Label()
 
166
        self.signature_key_id.set_selectable(True)
 
167
        align.add(self.signature_key_id)
 
168
        signature_box.attach(align, 2, 3, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
169
 
 
170
        align = gtk.Alignment(0.0, 0.5)
 
171
        self.signature_fingerprint_label = gtk.Label()
 
172
        self.signature_fingerprint_label.set_markup("<b>Fingerprint:</b>")
 
173
        align.add(self.signature_fingerprint_label)
 
174
        signature_box.attach(align, 1, 2, 2, 3, gtk.FILL, gtk.FILL)
 
175
 
 
176
        align = gtk.Alignment(0.0, 0.5)
 
177
        self.signature_fingerprint = gtk.Label()
 
178
        self.signature_fingerprint.set_selectable(True)
 
179
        align.add(self.signature_fingerprint)
 
180
        signature_box.attach(align, 2, 3, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
181
 
 
182
        align = gtk.Alignment(0.0, 0.5)
 
183
        self.signature_trust_label = gtk.Label()
 
184
        self.signature_trust_label.set_markup("<b>Trust:</b>")
 
185
        align.add(self.signature_trust_label)
 
186
        signature_box.attach(align, 1, 2, 3, 4, gtk.FILL, gtk.FILL)
 
187
 
 
188
        align = gtk.Alignment(0.0, 0.5)
 
189
        self.signature_trust = gtk.Label()
 
190
        self.signature_trust.set_selectable(True)
 
191
        align.add(self.signature_trust)
 
192
        signature_box.attach(align, 2, 3, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
193
 
 
194
        self.set_border_width(6)
 
195
        self.pack_start(signature_box, expand=False)
 
196
        self.show_all()
 
197
 
 
198
    def set_revision(self, revision):
 
199
        self.revision = revision
 
200
        revid = revision.revision_id
 
201
 
 
202
        if self.repository.has_signature_for_revision_id(revid):
 
203
            crypttext = self.repository.get_signature_text(revid)
 
204
            self.show_signature(crypttext)
 
205
        else:
 
206
            self.show_no_signature()
 
207
 
 
208
    def show_no_signature(self):
 
209
        self.signature_key_id_label.hide()
 
210
        self.signature_key_id.set_text("")
 
211
 
 
212
        self.signature_fingerprint_label.hide()
 
213
        self.signature_fingerprint.set_text("")
 
214
 
 
215
        self.signature_trust_label.hide()
 
216
        self.signature_trust.set_text("")
 
217
 
 
218
        self.signature_image.set_from_file(icon_path("sign-unknown.png"))
 
219
        self.signature_label.set_markup("<b>Authenticity unknown</b>\n" +
 
220
                                        "This revision has not been signed.")
 
221
 
 
222
    def show_signature(self, crypttext):
 
223
        key = seahorse.verify(crypttext)
 
224
 
 
225
        if key and key.is_available():
 
226
            if key.is_trusted():
 
227
                if key.get_display_name() == self.revision.committer:
 
228
                    self.signature_image.set_from_file(icon_path("sign-ok.png"))
 
229
                    self.signature_label.set_markup("<b>Authenticity confirmed</b>\n" +
 
230
                                                    "This revision has been signed with " +
 
231
                                                    "a trusted key.")
 
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
                                                    "Revision committer is not the same as signer.")
 
236
            else:
 
237
                self.signature_image.set_from_file(icon_path("sign-bad.png"))
 
238
                self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
 
239
                                                "This revision has been signed, but the " +
 
240
                                                "key is not trusted.")
 
241
        else:
 
242
            self.show_no_signature()
 
243
            self.signature_image.set_from_file(icon_path("sign-bad.png"))
 
244
            self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
 
245
                                            "Signature key not available.")
 
246
            return
 
247
 
 
248
        trust = key.get_trust()
 
249
 
 
250
        if trust <= seahorse.TRUST_NEVER:
 
251
            trust_text = 'never trusted'
 
252
        elif trust == seahorse.TRUST_UNKNOWN:
 
253
            trust_text = 'not trusted'
 
254
        elif trust == seahorse.TRUST_MARGINAL:
 
255
            trust_text = 'marginally trusted'
 
256
        elif trust == seahorse.TRUST_FULL:
 
257
            trust_text = 'fully trusted'
 
258
        elif trust == seahorse.TRUST_ULTIMATE:
 
259
            trust_text = 'ultimately trusted'
 
260
 
 
261
        self.signature_key_id_label.show()
 
262
        self.signature_key_id.set_text(key.get_id())
 
263
 
 
264
        fingerprint = key.get_fingerprint()
 
265
        if fingerprint == "":
 
266
            fingerprint = '<span foreground="dim grey">N/A</span>'
 
267
 
 
268
        self.signature_fingerprint_label.show()
 
269
        self.signature_fingerprint.set_markup(fingerprint)
 
270
 
 
271
        self.signature_trust_label.show()
 
272
        self.signature_trust.set_text('This key is ' + trust_text)
 
273
 
 
274
 
 
275
class RevisionView(gtk.Notebook):
31
276
    """ Custom widget for commit log details.
32
277
 
33
278
    A variety of bzr tools may need to implement such a thing. This is a
34
279
    start.
35
280
    """
36
281
 
37
 
    def __init__(self, revision=None, scroll=True, tags=[],
38
 
                 show_children=False, branch=None):
 
282
    __gproperties__ = {
 
283
        'branch': (
 
284
            gobject.TYPE_PYOBJECT,
 
285
            'Branch',
 
286
            'The branch holding the revision being displayed',
 
287
            gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE
 
288
        ),
 
289
 
 
290
        'revision': (
 
291
            gobject.TYPE_PYOBJECT,
 
292
            'Revision',
 
293
            'The revision being displayed',
 
294
            gobject.PARAM_READWRITE
 
295
        ),
 
296
 
 
297
        'children': (
 
298
            gobject.TYPE_PYOBJECT,
 
299
            'Children',
 
300
            'Child revisions',
 
301
            gobject.PARAM_READWRITE
 
302
        ),
 
303
 
 
304
        'file-id': (
 
305
            gobject.TYPE_PYOBJECT,
 
306
            'File Id',
 
307
            'The file id',
 
308
            gobject.PARAM_READWRITE
 
309
        )
 
310
    }
 
311
 
 
312
    def __init__(self, branch=None, repository=None):
39
313
        gtk.Notebook.__init__(self)
40
 
        self.show_children = show_children
 
314
 
 
315
        self._revision = None
 
316
        self._branch = branch
 
317
        if branch is not None:
 
318
            self._repository = branch.repository
 
319
        else:
 
320
            self._repository = repository
41
321
 
42
322
        self._create_general()
43
323
        self._create_relations()
44
 
        self._create_changes()
 
324
        # Disabled because testaments aren't verified yet:
 
325
        # if has_seahorse:
 
326
        #    self._create_signature()
 
327
        self._create_file_info_view()
 
328
        self._create_bugs()
 
329
 
 
330
        self.set_current_page(PAGE_GENERAL)
 
331
        self.connect_after('switch-page', self._switch_page_cb)
45
332
        
46
333
        self._show_callback = None
47
 
        self._go_callback = None
48
334
        self._clicked_callback = None
49
335
 
 
336
        self._revision = None
50
337
        self._branch = branch
51
338
 
52
 
        if revision is not None:
53
 
            self.set_revision(revision, tags=tags)
 
339
        self.update_tags()
 
340
 
 
341
        self.set_file_id(None)
 
342
 
 
343
    def do_get_property(self, property):
 
344
        if property.name == 'branch':
 
345
            return self._branch
 
346
        elif property.name == 'revision':
 
347
            return self._revision
 
348
        elif property.name == 'children':
 
349
            return self._children
 
350
        elif property.name == 'file-id':
 
351
            return self._file_id
 
352
        else:
 
353
            raise AttributeError, 'unknown property %s' % property.name
 
354
 
 
355
    def do_set_property(self, property, value):
 
356
        if property.name == 'branch':
 
357
            self._branch = value
 
358
        elif property.name == 'revision':
 
359
            self._set_revision(value)
 
360
        elif property.name == 'children':
 
361
            self.set_children(value)
 
362
        elif property.name == 'file-id':
 
363
            self._file_id = value
 
364
        else:
 
365
            raise AttributeError, 'unknown property %s' % property.name
54
366
 
55
367
    def set_show_callback(self, callback):
56
368
        self._show_callback = callback
57
369
 
58
 
    def set_go_callback(self, callback):
59
 
        self._go_callback = callback
60
 
 
61
 
    def set_revision(self, revision, tags=[], children=[]):
 
370
    def set_file_id(self, file_id):
 
371
        """Set a specific file id that we want to track.
 
372
 
 
373
        This just effects the display of a per-file commit message.
 
374
        If it is set to None, then all commit messages will be shown.
 
375
        """
 
376
        self.set_property('file-id', file_id)
 
377
 
 
378
    def set_revision(self, revision):
 
379
        if revision != self._revision:
 
380
            self.set_property('revision', revision)
 
381
 
 
382
    def get_revision(self):
 
383
        return self.get_property('revision')
 
384
 
 
385
    def _set_revision(self, revision):
 
386
        if revision is None: return
 
387
 
62
388
        self._revision = revision
63
 
        self.revision_id.set_text(revision.revision_id)
64
389
        if revision.committer is not None:
65
390
            self.committer.set_text(revision.committer)
66
391
        else:
77
402
        if revision.timestamp is not None:
78
403
            self.timestamp.set_text(format_date(revision.timestamp,
79
404
                                                revision.timezone))
80
 
        self.message_buffer.set_text(revision.message)
81
405
        try:
82
406
            self.branchnick_label.set_text(revision.properties['branch-nick'])
83
407
        except KeyError:
87
411
                                      self.parents_widgets,
88
412
                                      self.parents_table)
89
413
        
90
 
        if self.show_children:
91
 
            self._add_parents_or_children(children,
92
 
                                          self.children_widgets,
93
 
                                          self.children_table)
94
 
        
95
 
        self._add_tags(tags)
96
 
        self._set_diff()
97
 
 
98
 
    def _set_diff(self):
99
 
        parentid = self._revision.parent_ids[0]
100
 
        revid    = self._revision.revision_id
101
 
 
102
 
        (parent_tree, rev_tree) = self._branch.repository.revision_trees([parentid, 
103
 
                                                                   revid])
104
 
 
105
 
        s = StringIO()
106
 
        show_diff_trees(parent_tree, rev_tree, s, None)
107
 
        self.diff_buffer.set_text(s.getvalue().decode(sys.getdefaultencoding(), 'replace'))
 
414
        file_info = revision.properties.get('file-info', None)
 
415
        if file_info is not None:
 
416
            file_info = bdecode(file_info.encode('UTF-8'))
 
417
 
 
418
        if file_info:
 
419
            if self._file_id is None:
 
420
                text = []
 
421
                for fi in file_info:
 
422
                    text.append('%(path)s\n%(message)s' % fi)
 
423
                self.file_info_buffer.set_text('\n'.join(text))
 
424
                self.file_info_box.show()
 
425
            else:
 
426
                text = []
 
427
                for fi in file_info:
 
428
                    if fi['file_id'] == self._file_id:
 
429
                        text.append(fi['message'])
 
430
                if text:
 
431
                    self.file_info_buffer.set_text('\n'.join(text))
 
432
                    self.file_info_box.show()
 
433
                else:
 
434
                    self.file_info_box.hide()
 
435
        else:
 
436
            self.file_info_box.hide()
 
437
 
 
438
    def update_tags(self):
 
439
        if self._branch is not None and self._branch.supports_tags():
 
440
            self._tagdict = self._branch.tags.get_reverse_tag_dict()
 
441
        else:
 
442
            self._tagdict = {}
 
443
 
 
444
        self._add_tags()
 
445
 
 
446
    def _update_signature(self, widget, param):
 
447
        if self.get_current_page() == PAGE_SIGNATURE:
 
448
            self.signature_table.set_revision(self._revision)
 
449
 
 
450
    def _update_bugs(self, widget, param):
 
451
        self.bugs_page.set_revision(self._revision)
 
452
        label = self.get_tab_label(self.bugs_page)
 
453
        label.set_sensitive(self.bugs_page.get_num_bugs() != 0)
 
454
 
 
455
    def set_children(self, children):
 
456
        self._add_parents_or_children(children,
 
457
                                      self.children_widgets,
 
458
                                      self.children_table)
 
459
 
 
460
    def _switch_page_cb(self, notebook, page, page_num):
 
461
        if page_num == PAGE_SIGNATURE:
 
462
            self.signature_table.set_revision(self._revision)
 
463
 
 
464
 
108
465
 
109
466
    def _show_clicked_cb(self, widget, revid, parentid):
110
467
        """Callback for when the show button for a parent is clicked."""
112
469
 
113
470
    def _go_clicked_cb(self, widget, revid):
114
471
        """Callback for when the go button for a parent is clicked."""
115
 
        self._go_callback(revid)
116
 
 
117
 
    def _add_tags(self, tags):
 
472
 
 
473
    def _add_tags(self, *args):
 
474
        if self._revision is None:
 
475
            return
 
476
 
 
477
        if self._tagdict.has_key(self._revision.revision_id):
 
478
            tags = self._tagdict[self._revision.revision_id]
 
479
        else:
 
480
            tags = []
 
481
            
118
482
        if tags == []:
119
483
            self.tags_list.hide()
120
484
            self.tags_label.hide()
121
485
            return
122
486
 
123
 
        for widget in self.tags_widgets:
124
 
            self.tags_list.remove(widget)
125
 
 
126
 
        self.tags_widgets = []
127
 
 
128
 
        for tag in tags:
129
 
            widget = gtk.Label(tag)
130
 
            widget.set_selectable(True)
131
 
            self.tags_widgets.append(widget)
132
 
            self.tags_list.add(widget)
 
487
        self.tags_list.set_text(", ".join(tags))
 
488
 
133
489
        self.tags_list.show_all()
134
490
        self.tags_label.show_all()
135
491
        
164
520
                hbox.pack_start(button, expand=False, fill=True)
165
521
                button.show()
166
522
 
167
 
            if self._go_callback is not None:
168
 
                button = gtk.Button(revid)
169
 
                button.connect("clicked", self._go_clicked_cb, revid)
170
 
            else:
171
 
                button = gtk.Label(revid)
 
523
            button = gtk.Button(revid)
 
524
            button.connect("clicked",
 
525
                    lambda w, r: self.set_revision(self._repository.get_revision(r)), revid)
172
526
            button.set_use_underline(False)
173
527
            hbox.pack_start(button, expand=False, fill=True)
174
528
            button.show()
185
539
        vbox = gtk.VBox(False, 6)
186
540
        vbox.set_border_width(6)
187
541
        vbox.pack_start(self._create_parents(), expand=False, fill=True)
188
 
        if self.show_children:
189
 
            vbox.pack_start(self._create_children(), expand=False, fill=True)
 
542
        vbox.pack_start(self._create_children(), expand=False, fill=True)
190
543
        self.append_page(vbox, tab_label=gtk.Label("Relations"))
191
544
        vbox.show()
192
545
 
193
 
    def _create_changes(self):
194
 
        slm = gtksourceview.SourceLanguagesManager()
195
 
 
196
 
        self.diff_buffer = gtksourceview.SourceBuffer()
197
 
        self.diff_buffer.set_language(slm.get_language_from_mime_type("text/x-patch"))
198
 
        self.diff_buffer.set_highlight(True)
199
 
 
200
 
        sourceview = gtksourceview.SourceView(self.diff_buffer)
201
 
        sourceview.set_editable(False)
202
 
        sourceview.modify_font(pango.FontDescription("Monospace"))
203
 
 
204
 
        sourceview.show()
205
 
 
206
 
        win = gtk.ScrolledWindow()
207
 
        win.add(sourceview)
208
 
 
209
 
        self.append_page(win, tab_label=gtk.Label("Changes"))
210
 
        win.show()
211
 
        
 
546
    def _create_signature(self):
 
547
        self.signature_table = SignatureTab(self._repository)
 
548
        self.append_page(self.signature_table, tab_label=gtk.Label('Signature'))
 
549
        self.connect_after('notify::revision', self._update_signature)
 
550
 
212
551
    def _create_headers(self):
213
552
        self.table = gtk.Table(rows=5, columns=2)
214
553
        self.table.set_row_spacings(6)
224
563
        label.show()
225
564
 
226
565
        align = gtk.Alignment(0.0, 0.5)
227
 
        self.revision_id = gtk.Label()
228
 
        self.revision_id.set_selectable(True)
229
 
        align.add(self.revision_id)
 
566
        revision_id = gtk.Label()
 
567
        revision_id.set_selectable(True)
 
568
        self.connect('notify::revision', 
 
569
                lambda w, p: revision_id.set_text(self._revision.revision_id))
 
570
        align.add(revision_id)
230
571
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
231
572
        align.show()
232
 
        self.revision_id.show()
 
573
        revision_id.show()
233
574
 
234
575
        align = gtk.Alignment(1.0, 0.5)
235
576
        self.author_label = gtk.Label()
305
646
        self.tags_label.show()
306
647
 
307
648
        align = gtk.Alignment(0.0, 0.5)
308
 
        self.tags_list = gtk.VBox()
 
649
        self.tags_list = gtk.Label()
309
650
        align.add(self.tags_list)
310
651
        self.table.attach(align, 1, 2, 5, 6, gtk.EXPAND | gtk.FILL, gtk.FILL)
311
652
        align.show()
312
653
        self.tags_list.show()
313
 
        self.tags_widgets = []
 
654
 
 
655
        self.connect('notify::revision', self._add_tags)
314
656
 
315
657
        return self.table
316
 
 
317
658
    
318
659
    def _create_parents(self):
319
660
        hbox = gtk.HBox(True, 3)
350
691
        align.show()
351
692
 
352
693
        return table
353
 
    
354
 
 
355
694
 
356
695
    def _create_message_view(self):
357
 
        self.message_buffer = gtk.TextBuffer()
 
696
        msg_buffer = gtk.TextBuffer()
 
697
        self.connect('notify::revision',
 
698
                lambda w, p: msg_buffer.set_text(self._revision.message))
358
699
        window = gtk.ScrolledWindow()
359
700
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
360
701
        window.set_shadow_type(gtk.SHADOW_IN)
361
 
        tv = gtk.TextView(self.message_buffer)
 
702
        tv = gtk.TextView(msg_buffer)
362
703
        tv.set_editable(False)
363
704
        tv.set_wrap_mode(gtk.WRAP_WORD)
 
705
 
364
706
        tv.modify_font(pango.FontDescription("Monospace"))
365
707
        tv.show()
366
708
        window.add(tv)
367
709
        window.show()
368
710
        return window
369
711
 
 
712
    def _create_bugs(self):
 
713
        self.bugs_page = BugsTab()
 
714
        self.connect_after('notify::revision', self._update_bugs) 
 
715
        self.append_page(self.bugs_page, tab_label=gtk.Label('Bugs'))
 
716
 
 
717
    def _create_file_info_view(self):
 
718
        self.file_info_box = gtk.VBox(False, 6)
 
719
        self.file_info_box.set_border_width(6)
 
720
        self.file_info_buffer = gtk.TextBuffer()
 
721
        window = gtk.ScrolledWindow()
 
722
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
723
        window.set_shadow_type(gtk.SHADOW_IN)
 
724
        tv = gtk.TextView(self.file_info_buffer)
 
725
        tv.set_editable(False)
 
726
        tv.set_wrap_mode(gtk.WRAP_WORD)
 
727
        tv.modify_font(pango.FontDescription("Monospace"))
 
728
        tv.show()
 
729
        window.add(tv)
 
730
        window.show()
 
731
        self.file_info_box.pack_start(window)
 
732
        self.file_info_box.hide() # Only shown when there are per-file messages
 
733
        self.append_page(self.file_info_box, tab_label=gtk.Label('Per-file'))
 
734