/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 annotate/logview.py

  • Committer: Jelmer Vernooij
  • Date: 2006-09-30 10:21:43 UTC
  • Revision ID: jelmer@samba.org-20060930102143-c0ef64d6ca860c21
Merge some files from Olive and bzr-gtk.

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