/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 18:31:29 UTC
  • mto: This revision was merged to the branch mainline in revision 518.
  • Revision ID: jelmer@samba.org-20080629183129-syqvz3xm5gqagzsx
Fix use of smart_add.

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 webbrowser
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
 
 
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):
26
276
    """ Custom widget for commit log details.
27
277
 
28
278
    A variety of bzr tools may need to implement such a thing. This is a
29
279
    start.
30
280
    """
31
281
 
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)
 
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):
 
313
        gtk.Notebook.__init__(self)
 
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
 
321
 
 
322
        self._create_general()
 
323
        self._create_relations()
 
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)
 
332
        
 
333
        self._show_callback = None
 
334
        self._clicked_callback = None
 
335
 
 
336
        self._revision = None
 
337
        self._branch = branch
 
338
 
 
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
 
366
 
 
367
    def set_show_callback(self, callback):
 
368
        self._show_callback = callback
 
369
 
 
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)
41
377
 
42
378
    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)
 
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
 
 
388
        self._revision = revision
 
389
        if revision.committer is not None:
 
390
            self.committer.set_text(revision.committer)
 
391
        else:
 
392
            self.committer.set_text("")
 
393
        author = revision.properties.get('author', '')
 
394
        if author != '':
 
395
            self.author.set_text(author)
 
396
            self.author.show()
 
397
            self.author_label.show()
 
398
        else:
 
399
            self.author.hide()
 
400
            self.author_label.hide()
 
401
 
 
402
        if revision.timestamp is not None:
 
403
            self.timestamp.set_text(format_date(revision.timestamp,
 
404
                                                revision.timezone))
 
405
        try:
 
406
            self.branchnick_label.set_text(revision.properties['branch-nick'])
 
407
        except KeyError:
 
408
            self.branchnick_label.set_text("")
 
409
 
 
410
        self._add_parents_or_children(revision.parent_ids,
 
411
                                      self.parents_widgets,
 
412
                                      self.parents_table)
 
413
        
 
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
 
 
465
 
 
466
    def _show_clicked_cb(self, widget, revid, parentid):
 
467
        """Callback for when the show button for a parent is clicked."""
 
468
        self._show_callback(revid, parentid)
 
469
 
 
470
    def _go_clicked_cb(self, widget, revid):
 
471
        """Callback for when the go button for a parent is clicked."""
 
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 = []
53
481
            
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)
 
482
        if tags == []:
 
483
            self.tags_list.hide()
 
484
            self.tags_label.hide()
 
485
            return
 
486
 
 
487
        self.tags_list.set_text(", ".join(tags))
 
488
 
 
489
        self.tags_list.show_all()
 
490
        self.tags_label.show_all()
 
491
        
 
492
    def _add_parents_or_children(self, revids, widgets, table):
 
493
        while len(widgets) > 0:
 
494
            widget = widgets.pop()
 
495
            table.remove(widget)
 
496
        
 
497
        table.resize(max(len(revids), 1), 2)
 
498
 
 
499
        for idx, revid in enumerate(revids):
 
500
            align = gtk.Alignment(0.0, 0.0)
 
501
            widgets.append(align)
 
502
            table.attach(align, 1, 2, idx, idx + 1,
 
503
                                      gtk.EXPAND | gtk.FILL, gtk.FILL)
60
504
            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):
 
505
 
 
506
            hbox = gtk.HBox(False, spacing=6)
 
507
            align.add(hbox)
 
508
            hbox.show()
 
509
 
 
510
            image = gtk.Image()
 
511
            image.set_from_stock(
 
512
                gtk.STOCK_FIND, gtk.ICON_SIZE_SMALL_TOOLBAR)
 
513
            image.show()
 
514
 
 
515
            if self._show_callback is not None:
 
516
                button = gtk.Button()
 
517
                button.add(image)
 
518
                button.connect("clicked", self._show_clicked_cb,
 
519
                               self._revision.revision_id, revid)
 
520
                hbox.pack_start(button, expand=False, fill=True)
 
521
                button.show()
 
522
 
 
523
            button = gtk.Button(revid)
 
524
            button.connect("clicked",
 
525
                    lambda w, r: self.set_revision(self._repository.get_revision(r)), revid)
 
526
            button.set_use_underline(False)
 
527
            hbox.pack_start(button, expand=False, fill=True)
 
528
            button.show()
 
529
 
 
530
    def _create_general(self):
84
531
        vbox = gtk.VBox(False, 6)
85
532
        vbox.set_border_width(6)
86
533
        vbox.pack_start(self._create_headers(), expand=False, fill=True)
87
534
        vbox.pack_start(self._create_message_view())
88
 
        self.add_with_viewport(vbox)
89
 
        vbox.show()
 
535
        self.append_page(vbox, tab_label=gtk.Label("General"))
 
536
        vbox.show()
 
537
 
 
538
    def _create_relations(self):
 
539
        vbox = gtk.VBox(False, 6)
 
540
        vbox.set_border_width(6)
 
541
        vbox.pack_start(self._create_parents(), expand=False, fill=True)
 
542
        vbox.pack_start(self._create_children(), expand=False, fill=True)
 
543
        self.append_page(vbox, tab_label=gtk.Label("Relations"))
 
544
        vbox.show()
 
545
 
 
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)
90
550
 
91
551
    def _create_headers(self):
92
 
        self.table = gtk.Table(rows=4, columns=2)
 
552
        self.table = gtk.Table(rows=5, columns=2)
93
553
        self.table.set_row_spacings(6)
94
554
        self.table.set_col_spacings(6)
95
555
        self.table.show()
96
 
        
 
556
 
 
557
        align = gtk.Alignment(1.0, 0.5)
 
558
        label = gtk.Label()
 
559
        label.set_markup("<b>Revision Id:</b>")
 
560
        align.add(label)
 
561
        self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
562
        align.show()
 
563
        label.show()
 
564
 
 
565
        align = gtk.Alignment(0.0, 0.5)
 
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)
 
571
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
572
        align.show()
 
573
        revision_id.show()
 
574
 
 
575
        align = gtk.Alignment(1.0, 0.5)
 
576
        self.author_label = gtk.Label()
 
577
        self.author_label.set_markup("<b>Author:</b>")
 
578
        align.add(self.author_label)
 
579
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
580
        align.show()
 
581
        self.author_label.show()
 
582
 
 
583
        align = gtk.Alignment(0.0, 0.5)
 
584
        self.author = gtk.Label()
 
585
        self.author.set_selectable(True)
 
586
        align.add(self.author)
 
587
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
588
        align.show()
 
589
        self.author.show()
 
590
        self.author.hide()
 
591
 
97
592
        align = gtk.Alignment(1.0, 0.5)
98
593
        label = gtk.Label()
99
594
        label.set_markup("<b>Committer:</b>")
100
595
        align.add(label)
101
 
        self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
596
        self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
102
597
        align.show()
103
598
        label.show()
104
599
 
106
601
        self.committer = gtk.Label()
107
602
        self.committer.set_selectable(True)
108
603
        align.add(self.committer)
109
 
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
604
        self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
110
605
        align.show()
111
606
        self.committer.show()
112
607
 
 
608
        align = gtk.Alignment(0.0, 0.5)
 
609
        label = gtk.Label()
 
610
        label.set_markup("<b>Branch nick:</b>")
 
611
        align.add(label)
 
612
        self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
 
613
        label.show()
 
614
        align.show()
 
615
 
 
616
        align = gtk.Alignment(0.0, 0.5)
 
617
        self.branchnick_label = gtk.Label()
 
618
        self.branchnick_label.set_selectable(True)
 
619
        align.add(self.branchnick_label)
 
620
        self.table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
621
        self.branchnick_label.show()
 
622
        align.show()
 
623
 
113
624
        align = gtk.Alignment(1.0, 0.5)
114
625
        label = gtk.Label()
115
626
        label.set_markup("<b>Timestamp:</b>")
116
627
        align.add(label)
117
 
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
628
        self.table.attach(align, 0, 1, 4, 5, gtk.FILL, gtk.FILL)
118
629
        align.show()
119
630
        label.show()
120
631
 
122
633
        self.timestamp = gtk.Label()
123
634
        self.timestamp.set_selectable(True)
124
635
        align.add(self.timestamp)
125
 
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
636
        self.table.attach(align, 1, 2, 4, 5, gtk.EXPAND | gtk.FILL, gtk.FILL)
126
637
        align.show()
127
638
        self.timestamp.show()
128
639
 
129
640
        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)
 
641
        self.tags_label = gtk.Label()
 
642
        self.tags_label.set_markup("<b>Tags:</b>")
 
643
        align.add(self.tags_label)
134
644
        align.show()
135
 
        label.show()
 
645
        self.table.attach(align, 0, 1, 5, 6, gtk.FILL, gtk.FILL)
 
646
        self.tags_label.show()
136
647
 
137
648
        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)
 
649
        self.tags_list = gtk.Label()
 
650
        align.add(self.tags_list)
 
651
        self.table.attach(align, 1, 2, 5, 6, gtk.EXPAND | gtk.FILL, gtk.FILL)
142
652
        align.show()
143
 
        self.revision_id.show()
 
653
        self.tags_list.show()
 
654
 
 
655
        self.connect('notify::revision', self._add_tags)
144
656
 
145
657
        return self.table
 
658
    
 
659
    def _create_parents(self):
 
660
        hbox = gtk.HBox(True, 3)
 
661
        
 
662
        self.parents_table = self._create_parents_or_children_table(
 
663
            "<b>Parents:</b>")
 
664
        self.parents_widgets = []
 
665
        hbox.pack_start(self.parents_table)
 
666
 
 
667
        hbox.show()
 
668
        return hbox
 
669
 
 
670
    def _create_children(self):
 
671
        hbox = gtk.HBox(True, 3)
 
672
        self.children_table = self._create_parents_or_children_table(
 
673
            "<b>Children:</b>")
 
674
        self.children_widgets = []
 
675
        hbox.pack_start(self.children_table)
 
676
        hbox.show()
 
677
        return hbox
 
678
        
 
679
    def _create_parents_or_children_table(self, text):
 
680
        table = gtk.Table(rows=1, columns=2)
 
681
        table.set_row_spacings(3)
 
682
        table.set_col_spacings(6)
 
683
        table.show()
 
684
 
 
685
        label = gtk.Label()
 
686
        label.set_markup(text)
 
687
        align = gtk.Alignment(0.0, 0.5)
 
688
        align.add(label)
 
689
        table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
690
        label.show()
 
691
        align.show()
 
692
 
 
693
        return table
146
694
 
147
695
    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
 
696
        msg_buffer = gtk.TextBuffer()
 
697
        self.connect('notify::revision',
 
698
                lambda w, p: msg_buffer.set_text(self._revision.message))
 
699
        window = gtk.ScrolledWindow()
 
700
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
701
        window.set_shadow_type(gtk.SHADOW_IN)
 
702
        tv = gtk.TextView(msg_buffer)
 
703
        tv.set_editable(False)
 
704
        tv.set_wrap_mode(gtk.WRAP_WORD)
 
705
 
 
706
        tv.modify_font(pango.FontDescription("Monospace"))
 
707
        tv.show()
 
708
        window.add(tv)
 
709
        window.show()
 
710
        return window
 
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'))
155
734