/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
0.1.1 by Dan Loda
First working version of xannotate.
1
# Copyright (C) 2005 Dan Loda <danloda@gmail.com>
175 by Jelmer Vernooij
Add very simple gmissing command.
2
# Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org>
0.1.1 by Dan Loda
First working version of xannotate.
3
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18
import pygtk
19
pygtk.require("2.0")
20
import gtk
21
import pango
399.1.9 by Daniel Schierbeck
Merged with trunk.
22
import gobject
423.14.1 by Jelmer Vernooij
Add bugs tab to display bug status change metadata.
23
import subprocess
0.1.1 by Dan Loda
First working version of xannotate.
24
399.1.19 by Jelmer Vernooij
Add utility function for finding icon paths.
25
from bzrlib.plugins.gtk import icon_path
0.1.1 by Dan Loda
First working version of xannotate.
26
from bzrlib.osutils import format_date
412.1.8 by Daniel Schierbeck
Only import the bdecode function from the bzrlib.util.bencode package.
27
from bzrlib.util.bencode import bdecode
0.1.1 by Dan Loda
First working version of xannotate.
28
399.3.27 by Daniel Schierbeck
Only show Signature tab if DBus and Seahorse are installed.
29
try:
399.3.29 by Daniel Schierbeck
Renamed the crypt module to seahorse.
30
    from bzrlib.plugins.gtk import seahorse
399.3.27 by Daniel Schierbeck
Only show Signature tab if DBus and Seahorse are installed.
31
except ImportError:
399.3.29 by Daniel Schierbeck
Renamed the crypt module to seahorse.
32
    has_seahorse = False
399.3.27 by Daniel Schierbeck
Only show Signature tab if DBus and Seahorse are installed.
33
else:
399.3.29 by Daniel Schierbeck
Renamed the crypt module to seahorse.
34
    has_seahorse = True
399.3.27 by Daniel Schierbeck
Only show Signature tab if DBus and Seahorse are installed.
35
423.14.2 by Jelmer Vernooij
Move bugs tab to separate widget.
36
def _open_link(widget, uri):
37
    subprocess.Popen(['sensible-browser', uri], close_fds=True)
38
39
gtk.link_button_set_uri_hook(_open_link)
40
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
41
class BugsTab(gtk.VBox):
399.3.1 by Daniel Schierbeck
Made the key id label disappear when the revision is not signed.
42
423.14.2 by Jelmer Vernooij
Move bugs tab to separate widget.
43
    def __init__(self):
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
44
        super(BugsTab, self).__init__(False, 6)
450.6.11 by Daniel Schierbeck
Made Bugs page always visible, but only sensitive when the revision has bug associations.
45
    
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
46
        table = gtk.Table(rows=2, columns=2)
47
48
        table.set_row_spacings(6)
450.6.7 by Daniel Schierbeck
Improved spacing in the bugs page.
49
        table.set_col_spacing(0, 16)
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
50
51
        image = gtk.Image()
52
        image.set_from_file(icon_path("bug.png"))
53
        table.attach(image, 0, 1, 0, 1, gtk.FILL)
54
450.6.2 by Daniel Schierbeck
Further improved the look of the bug tab.
55
        align = gtk.Alignment(0.0, 0.1)
450.6.12 by Daniel Schierbeck
Changed Bugs page labels.
56
        self.label = gtk.Label()
57
        align.add(self.label)
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
58
        table.attach(align, 1, 2, 0, 1, gtk.FILL)
59
60
        treeview = self.construct_treeview()
61
        table.attach(treeview, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND)
62
63
        self.set_border_width(6)
64
        self.pack_start(table, expand=False)
65
450.6.12 by Daniel Schierbeck
Changed Bugs page labels.
66
        self.clear()
450.6.11 by Daniel Schierbeck
Made Bugs page always visible, but only sensitive when the revision has bug associations.
67
        self.show_all()
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
68
450.6.4 by Daniel Schierbeck
Moved bug parsing code into the bug page itself.
69
    def set_revision(self, revision):
70
        if revision is None:
71
            return
72
73
        self.clear()
450.6.5 by Daniel Schierbeck
Simplified bug parsing.
74
        bugs_text = revision.properties.get('bugs', '')
75
        for bugline in bugs_text.splitlines():
450.6.4 by Daniel Schierbeck
Moved bug parsing code into the bug page itself.
76
                (url, status) = bugline.split(" ")
450.6.10 by Daniel Schierbeck
Only show fixed bugs in Bugs page.
77
                if status == "fixed":
78
                    self.add_bug(url, status)
450.6.12 by Daniel Schierbeck
Changed Bugs page labels.
79
        
80
        if self.num_bugs == 0:
81
            return
82
        elif self.num_bugs == 1:
83
            label = "bug"
84
        else:
85
            label = "bugs"
86
87
        self.label.set_markup("<b>Bugs fixed</b>\n" +
88
                              "This revision claims to fix " +
89
                              "%d %s." % (self.num_bugs, label))
450.6.4 by Daniel Schierbeck
Moved bug parsing code into the bug page itself.
90
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
91
    def construct_treeview(self):
92
        self.bugs = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
93
        self.treeview = gtk.TreeView(self.bugs)
450.6.8 by Daniel Schierbeck
Removed status column from bug table.
94
        self.treeview.set_headers_visible(False)
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
95
96
        uri_column = gtk.TreeViewColumn('Bug URI', gtk.CellRendererText(), text=0)
97
        self.treeview.append_column(uri_column)
98
450.6.9 by Daniel Schierbeck
Made it possible to open bugs in the browser.
99
        self.treeview.connect('row-activated', self.on_row_activated)
100
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
101
        win = gtk.ScrolledWindow()
102
        win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
103
        win.set_shadow_type(gtk.SHADOW_IN)
104
        win.add(self.treeview)
105
106
        return win
423.14.2 by Jelmer Vernooij
Move bugs tab to separate widget.
107
108
    def clear(self):
450.6.11 by Daniel Schierbeck
Made Bugs page always visible, but only sensitive when the revision has bug associations.
109
        self.num_bugs = 0
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
110
        self.bugs.clear()
450.6.11 by Daniel Schierbeck
Made Bugs page always visible, but only sensitive when the revision has bug associations.
111
        self.set_sensitive(False)
450.6.12 by Daniel Schierbeck
Changed Bugs page labels.
112
        self.label.set_markup("<b>No bugs fixed</b>\n" +
113
                              "This revision does not claim to fix any bugs.")
423.14.2 by Jelmer Vernooij
Move bugs tab to separate widget.
114
115
    def add_bug(self, url, status):
450.6.11 by Daniel Schierbeck
Made Bugs page always visible, but only sensitive when the revision has bug associations.
116
        self.num_bugs += 1
450.6.1 by Daniel Schierbeck
Made bug tab prettier.
117
        self.bugs.append([url, status])
450.6.11 by Daniel Schierbeck
Made Bugs page always visible, but only sensitive when the revision has bug associations.
118
        self.set_sensitive(True)
119
120
    def get_num_bugs(self):
121
        return self.num_bugs
423.14.2 by Jelmer Vernooij
Move bugs tab to separate widget.
122
450.6.9 by Daniel Schierbeck
Made it possible to open bugs in the browser.
123
    def on_row_activated(self, treeview, path, column):
124
        uri = self.bugs.get_value(self.bugs.get_iter(path), 0)
125
        _open_link(self, uri)
126
423.14.2 by Jelmer Vernooij
Move bugs tab to separate widget.
127
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
128
class SignatureTab(gtk.VBox):
399.3.1 by Daniel Schierbeck
Made the key id label disappear when the revision is not signed.
129
399.3.17 by Daniel Schierbeck
Moved signature handling into signature widget.
130
    def __init__(self, repository):
399.3.8 by Daniel Schierbeck
Moved crypt code into a Key class.
131
        self.key = None
399.3.23 by Daniel Schierbeck
Added comparison with revision committer.
132
        self.revision = None
399.3.17 by Daniel Schierbeck
Moved signature handling into signature widget.
133
        self.repository = repository
399.3.8 by Daniel Schierbeck
Moved crypt code into a Key class.
134
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
135
        super(SignatureTab, self).__init__(False, 6)
399.3.5 by Daniel Schierbeck
Added support for key fingerprints.
136
        signature_box = gtk.Table(rows=3, columns=3)
399.3.21 by Daniel Schierbeck
Played a bit with the spacings.
137
        signature_box.set_col_spacing(0, 16)
138
        signature_box.set_col_spacing(1, 12)
399.3.19 by Daniel Schierbeck
Increased row spacing in signature tab.
139
        signature_box.set_row_spacings(6)
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
140
141
        self.signature_image = gtk.Image()
142
        signature_box.attach(self.signature_image, 0, 1, 0, 1, gtk.FILL)
143
399.3.21 by Daniel Schierbeck
Played a bit with the spacings.
144
        align = gtk.Alignment(0.0, 0.1)
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
145
        self.signature_label = gtk.Label()
399.3.2 by Daniel Schierbeck
Moved key id label to the right.
146
        align.add(self.signature_label)
147
        signature_box.attach(align, 1, 3, 0, 1, gtk.FILL)
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
148
399.3.21 by Daniel Schierbeck
Played a bit with the spacings.
149
        align = gtk.Alignment(0.0, 0.5)
399.3.1 by Daniel Schierbeck
Made the key id label disappear when the revision is not signed.
150
        self.signature_key_id_label = gtk.Label()
151
        self.signature_key_id_label.set_markup("<b>Key Id:</b>")
152
        align.add(self.signature_key_id_label)
399.3.2 by Daniel Schierbeck
Moved key id label to the right.
153
        signature_box.attach(align, 1, 2, 1, 2, gtk.FILL, gtk.FILL)
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
154
155
        align = gtk.Alignment(0.0, 0.5)
156
        self.signature_key_id = gtk.Label()
157
        self.signature_key_id.set_selectable(True)
158
        align.add(self.signature_key_id)
399.3.2 by Daniel Schierbeck
Moved key id label to the right.
159
        signature_box.attach(align, 2, 3, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
160
399.3.21 by Daniel Schierbeck
Played a bit with the spacings.
161
        align = gtk.Alignment(0.0, 0.5)
399.3.5 by Daniel Schierbeck
Added support for key fingerprints.
162
        self.signature_fingerprint_label = gtk.Label()
163
        self.signature_fingerprint_label.set_markup("<b>Fingerprint:</b>")
164
        align.add(self.signature_fingerprint_label)
165
        signature_box.attach(align, 1, 2, 2, 3, gtk.FILL, gtk.FILL)
166
167
        align = gtk.Alignment(0.0, 0.5)
168
        self.signature_fingerprint = gtk.Label()
169
        self.signature_fingerprint.set_selectable(True)
170
        align.add(self.signature_fingerprint)
171
        signature_box.attach(align, 2, 3, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
172
399.3.6 by Daniel Schierbeck
Added support for key trust.
173
        align = gtk.Alignment(0.0, 0.5)
399.3.21 by Daniel Schierbeck
Played a bit with the spacings.
174
        self.signature_trust_label = gtk.Label()
175
        self.signature_trust_label.set_markup("<b>Trust:</b>")
176
        align.add(self.signature_trust_label)
177
        signature_box.attach(align, 1, 2, 3, 4, gtk.FILL, gtk.FILL)
178
179
        align = gtk.Alignment(0.0, 0.5)
399.3.6 by Daniel Schierbeck
Added support for key trust.
180
        self.signature_trust = gtk.Label()
181
        self.signature_trust.set_selectable(True)
182
        align.add(self.signature_trust)
183
        signature_box.attach(align, 2, 3, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
184
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
185
        self.set_border_width(6)
186
        self.pack_start(signature_box, expand=False)
187
        self.show_all()
188
399.3.17 by Daniel Schierbeck
Moved signature handling into signature widget.
189
    def set_revision(self, revision):
190
        self.revision = revision
191
        revid = revision.revision_id
192
193
        if self.repository.has_signature_for_revision_id(revid):
399.3.30 by Daniel Schierbeck
Fixed some naming issues.
194
            crypttext = self.repository.get_signature_text(revid)
195
            self.show_signature(crypttext)
399.3.17 by Daniel Schierbeck
Moved signature handling into signature widget.
196
        else:
197
            self.show_no_signature()
198
399.1.16 by Jelmer Vernooij
Move logic showing images to the SignatureTab widget.
199
    def show_no_signature(self):
399.3.1 by Daniel Schierbeck
Made the key id label disappear when the revision is not signed.
200
        self.signature_key_id_label.hide()
399.1.16 by Jelmer Vernooij
Move logic showing images to the SignatureTab widget.
201
        self.signature_key_id.set_text("")
399.3.6 by Daniel Schierbeck
Added support for key trust.
202
399.3.5 by Daniel Schierbeck
Added support for key fingerprints.
203
        self.signature_fingerprint_label.hide()
204
        self.signature_fingerprint.set_text("")
399.3.6 by Daniel Schierbeck
Added support for key trust.
205
206
        self.signature_trust_label.hide()
207
        self.signature_trust.set_text("")
208
399.1.19 by Jelmer Vernooij
Add utility function for finding icon paths.
209
        self.signature_image.set_from_file(icon_path("sign-unknown.png"))
399.3.9 by Daniel Schierbeck
Added headings.
210
        self.signature_label.set_markup("<b>Authenticity unknown</b>\n" +
211
                                        "This revision has not been signed.")
399.1.16 by Jelmer Vernooij
Move logic showing images to the SignatureTab widget.
212
399.3.30 by Daniel Schierbeck
Fixed some naming issues.
213
    def show_signature(self, crypttext):
214
        key = seahorse.verify(crypttext)
399.3.3 by Daniel Schierbeck
Made the signature code use the Seahorse D-Bus service.
215
450.5.3 by Daniel Schierbeck
Made the signature checking code not try to discover the signature key.
216
        if key and key.is_available():
399.3.23 by Daniel Schierbeck
Added comparison with revision committer.
217
            if key.is_trusted():
218
                if key.get_display_name() == self.revision.committer:
219
                    self.signature_image.set_from_file(icon_path("sign-ok.png"))
220
                    self.signature_label.set_markup("<b>Authenticity confirmed</b>\n" +
221
                                                    "This revision has been signed with " +
222
                                                    "a trusted key.")
223
                else:
224
                    self.signature_image.set_from_file(icon_path("sign-bad.png"))
225
                    self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
226
                                                    "Revision committer is not the same as signer.")
227
            else:
228
                self.signature_image.set_from_file(icon_path("sign-bad.png"))
229
                self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
230
                                                "This revision has been signed, but the " +
231
                                                "key is not trusted.")
232
        else:
399.3.11 by Daniel Schierbeck
Fixed error with unavailable keys.
233
            self.show_no_signature()
234
            self.signature_image.set_from_file(icon_path("sign-bad.png"))
399.3.13 by Daniel Schierbeck
Changed wording of authenticity heading.
235
            self.signature_label.set_markup("<b>Authenticity cannot be confirmed</b>\n" +
399.3.12 by Daniel Schierbeck
Fixed typo.
236
                                            "Signature key not available.")
399.3.11 by Daniel Schierbeck
Fixed error with unavailable keys.
237
            return
238
399.3.8 by Daniel Schierbeck
Moved crypt code into a Key class.
239
        trust = key.get_trust()
399.3.6 by Daniel Schierbeck
Added support for key trust.
240
399.3.29 by Daniel Schierbeck
Renamed the crypt module to seahorse.
241
        if trust <= seahorse.TRUST_NEVER:
399.3.6 by Daniel Schierbeck
Added support for key trust.
242
            trust_text = 'never trusted'
399.3.29 by Daniel Schierbeck
Renamed the crypt module to seahorse.
243
        elif trust == seahorse.TRUST_UNKNOWN:
399.3.6 by Daniel Schierbeck
Added support for key trust.
244
            trust_text = 'not trusted'
399.3.29 by Daniel Schierbeck
Renamed the crypt module to seahorse.
245
        elif trust == seahorse.TRUST_MARGINAL:
399.3.6 by Daniel Schierbeck
Added support for key trust.
246
            trust_text = 'marginally trusted'
399.3.29 by Daniel Schierbeck
Renamed the crypt module to seahorse.
247
        elif trust == seahorse.TRUST_FULL:
399.3.6 by Daniel Schierbeck
Added support for key trust.
248
            trust_text = 'fully trusted'
399.3.29 by Daniel Schierbeck
Renamed the crypt module to seahorse.
249
        elif trust == seahorse.TRUST_ULTIMATE:
399.3.6 by Daniel Schierbeck
Added support for key trust.
250
            trust_text = 'ultimately trusted'
399.3.5 by Daniel Schierbeck
Added support for key fingerprints.
251
399.3.1 by Daniel Schierbeck
Made the key id label disappear when the revision is not signed.
252
        self.signature_key_id_label.show()
399.3.8 by Daniel Schierbeck
Moved crypt code into a Key class.
253
        self.signature_key_id.set_text(key.get_id())
399.3.5 by Daniel Schierbeck
Added support for key fingerprints.
254
399.3.15 by Daniel Schierbeck
Made the fingerprint label read N/A when no fingerprint is available.
255
        fingerprint = key.get_fingerprint()
256
        if fingerprint == "":
399.3.18 by Daniel Schierbeck
Made empty fingerprint text grey.
257
            fingerprint = '<span foreground="dim grey">N/A</span>'
399.3.15 by Daniel Schierbeck
Made the fingerprint label read N/A when no fingerprint is available.
258
399.3.5 by Daniel Schierbeck
Added support for key fingerprints.
259
        self.signature_fingerprint_label.show()
399.3.18 by Daniel Schierbeck
Made empty fingerprint text grey.
260
        self.signature_fingerprint.set_markup(fingerprint)
399.3.3 by Daniel Schierbeck
Made the signature code use the Seahorse D-Bus service.
261
399.3.6 by Daniel Schierbeck
Added support for key trust.
262
        self.signature_trust_label.show()
263
        self.signature_trust.set_text('This key is ' + trust_text)
399.3.17 by Daniel Schierbeck
Moved signature handling into signature widget.
264
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
265
330.3.1 by Daniel Schierbeck
Renamed logview 'revisionview'.
266
class RevisionView(gtk.Notebook):
0.1.1 by Dan Loda
First working version of xannotate.
267
    """ Custom widget for commit log details.
268
269
    A variety of bzr tools may need to implement such a thing. This is a
270
    start.
271
    """
272
412.1.1 by Daniel Schierbeck
Added branch and revision properties to the revisionview widget.
273
    __gproperties__ = {
274
        'branch': (
275
            gobject.TYPE_PYOBJECT,
276
            'Branch',
277
            'The branch holding the revision being displayed',
278
            gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE
279
        ),
280
281
        'revision': (
282
            gobject.TYPE_PYOBJECT,
283
            'Revision',
284
            'The revision being displayed',
412.1.2 by Daniel Schierbeck
Moved retrieval of tags into the revisionview itself.
285
            gobject.PARAM_READWRITE
412.1.7 by Daniel Schierbeck
Added file-id property to the revisionview.
286
        ),
287
412.1.13 by Daniel Schierbeck
Re-added support for displaying the children of a revision.
288
        'children': (
289
            gobject.TYPE_PYOBJECT,
290
            'Children',
291
            'Child revisions',
292
            gobject.PARAM_READWRITE
293
        ),
294
412.1.7 by Daniel Schierbeck
Added file-id property to the revisionview.
295
        'file-id': (
296
            gobject.TYPE_PYOBJECT,
297
            'File Id',
298
            'The file id',
299
            gobject.PARAM_READWRITE
412.1.1 by Daniel Schierbeck
Added branch and revision properties to the revisionview widget.
300
        )
301
    }
302
303
412.1.2 by Daniel Schierbeck
Moved retrieval of tags into the revisionview itself.
304
    def __init__(self, branch=None):
324.2.1 by Daniel Schierbeck
Turned the logview into a notebook.
305
        gtk.Notebook.__init__(self)
324.2.4 by Daniel Schierbeck
Added 'Changes' page to logview.
306
399.3.17 by Daniel Schierbeck
Moved signature handling into signature widget.
307
        self._revision = None
308
        self._branch = branch
309
324.2.1 by Daniel Schierbeck
Turned the logview into a notebook.
310
        self._create_general()
311
        self._create_relations()
399.3.29 by Daniel Schierbeck
Renamed the crypt module to seahorse.
312
        if has_seahorse:
399.3.27 by Daniel Schierbeck
Only show Signature tab if DBus and Seahorse are installed.
313
            self._create_signature()
278.1.45 by John Arbash Meinel
Switch to a new tab for per-file messages.
314
        self._create_file_info_view()
423.14.1 by Jelmer Vernooij
Add bugs tab to display bug status change metadata.
315
        self._create_bugs()
324.2.9 by Daniel Schierbeck
Made 'General' the default page of the logview.
316
317
        self.set_current_page(0)
324.2.4 by Daniel Schierbeck
Added 'Changes' page to logview.
318
        
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
319
        self._show_callback = None
320
        self._clicked_callback = None
0.1.1 by Dan Loda
First working version of xannotate.
321
412.1.9 by Daniel Schierbeck
Removed the use of RevisionView.set_go_callback().
322
        self._revision = None
324.2.4 by Daniel Schierbeck
Added 'Changes' page to logview.
323
        self._branch = branch
324
423.7.4 by Daniel Schierbeck
Made revisionview and branchview update when a tag is added.
325
        self.update_tags()
412.1.2 by Daniel Schierbeck
Moved retrieval of tags into the revisionview itself.
326
278.1.3 by John Arbash Meinel
Have the ability to tell the log view that we only care about a specific file_id.
327
        self.set_file_id(None)
0.1.1 by Dan Loda
First working version of xannotate.
328
412.1.1 by Daniel Schierbeck
Added branch and revision properties to the revisionview widget.
329
    def do_get_property(self, property):
330
        if property.name == 'branch':
331
            return self._branch
332
        elif property.name == 'revision':
333
            return self._revision
412.1.13 by Daniel Schierbeck
Re-added support for displaying the children of a revision.
334
        elif property.name == 'children':
335
            return self._children
412.1.7 by Daniel Schierbeck
Added file-id property to the revisionview.
336
        elif property.name == 'file-id':
337
            return self._file_id
412.1.1 by Daniel Schierbeck
Added branch and revision properties to the revisionview widget.
338
        else:
339
            raise AttributeError, 'unknown property %s' % property.name
340
341
    def do_set_property(self, property, value):
342
        if property.name == 'branch':
343
            self._branch = value
344
        elif property.name == 'revision':
345
            self._set_revision(value)
412.1.13 by Daniel Schierbeck
Re-added support for displaying the children of a revision.
346
        elif property.name == 'children':
347
            self.set_children(value)
412.1.7 by Daniel Schierbeck
Added file-id property to the revisionview.
348
        elif property.name == 'file-id':
349
            self._file_id = value
412.1.1 by Daniel Schierbeck
Added branch and revision properties to the revisionview widget.
350
        else:
351
            raise AttributeError, 'unknown property %s' % property.name
352
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
353
    def set_show_callback(self, callback):
354
        self._show_callback = callback
355
278.1.3 by John Arbash Meinel
Have the ability to tell the log view that we only care about a specific file_id.
356
    def set_file_id(self, file_id):
357
        """Set a specific file id that we want to track.
358
359
        This just effects the display of a per-file commit message.
360
        If it is set to None, then all commit messages will be shown.
361
        """
412.1.7 by Daniel Schierbeck
Added file-id property to the revisionview.
362
        self.set_property('file-id', file_id)
278.1.3 by John Arbash Meinel
Have the ability to tell the log view that we only care about a specific file_id.
363
412.1.13 by Daniel Schierbeck
Re-added support for displaying the children of a revision.
364
    def set_revision(self, revision):
412.1.9 by Daniel Schierbeck
Removed the use of RevisionView.set_go_callback().
365
        if revision != self._revision:
366
            self.set_property('revision', revision)
367
368
    def get_revision(self):
369
        return self.get_property('revision')
412.1.1 by Daniel Schierbeck
Added branch and revision properties to the revisionview widget.
370
412.1.15 by Daniel Schierbeck
Removed redundant method argument.
371
    def _set_revision(self, revision):
412.1.1 by Daniel Schierbeck
Added branch and revision properties to the revisionview widget.
372
        if revision is None: return
373
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
374
        self._revision = revision
192 by Jelmer Vernooij
Allow committer to be None.
375
        if revision.committer is not None:
376
            self.committer.set_text(revision.committer)
377
        else:
378
            self.committer.set_text("")
259 by Aaron Bentley
Add author support to gannotate and log viewer
379
        author = revision.properties.get('author', '')
380
        if author != '':
381
            self.author.set_text(author)
382
            self.author.show()
383
            self.author_label.show()
384
        else:
385
            self.author.hide()
386
            self.author_label.hide()
387
197 by Jelmer Vernooij
Fix some warnings when displaying ghost revisions. Reported by John.
388
        if revision.timestamp is not None:
389
            self.timestamp.set_text(format_date(revision.timestamp,
390
                                                revision.timezone))
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
391
        try:
392
            self.branchnick_label.set_text(revision.properties['branch-nick'])
393
        except KeyError:
394
            self.branchnick_label.set_text("")
395
256.2.23 by Gary van der Merwe
Show Children
396
        self._add_parents_or_children(revision.parent_ids,
397
                                      self.parents_widgets,
398
                                      self.parents_table)
399
        
278.1.3 by John Arbash Meinel
Have the ability to tell the log view that we only care about a specific file_id.
400
        file_info = revision.properties.get('file-info', None)
278.1.2 by John Arbash Meinel
Add an extra box that pops up when we have per-file information.
401
        if file_info is not None:
412.1.8 by Daniel Schierbeck
Only import the bdecode function from the bzrlib.util.bencode package.
402
            file_info = bdecode(file_info.encode('UTF-8'))
278.1.2 by John Arbash Meinel
Add an extra box that pops up when we have per-file information.
403
404
        if file_info:
278.1.3 by John Arbash Meinel
Have the ability to tell the log view that we only care about a specific file_id.
405
            if self._file_id is None:
406
                text = []
407
                for fi in file_info:
408
                    text.append('%(path)s\n%(message)s' % fi)
409
                self.file_info_buffer.set_text('\n'.join(text))
410
                self.file_info_box.show()
411
            else:
412
                text = []
413
                for fi in file_info:
414
                    if fi['file_id'] == self._file_id:
415
                        text.append(fi['message'])
416
                if text:
417
                    self.file_info_buffer.set_text('\n'.join(text))
418
                    self.file_info_box.show()
419
                else:
420
                    self.file_info_box.hide()
278.1.2 by John Arbash Meinel
Add an extra box that pops up when we have per-file information.
421
        else:
422
            self.file_info_box.hide()
423
423.7.4 by Daniel Schierbeck
Made revisionview and branchview update when a tag is added.
424
    def update_tags(self):
425
        if self._branch is not None and self._branch.supports_tags():
426
            self._tagdict = self._branch.tags.get_reverse_tag_dict()
427
        else:
428
            self._tagdict = {}
429
430
        self._add_tags()
431
399.1.13 by Daniel Schierbeck
Merged with mainline.
432
    def _update_signature(self, widget, param):
399.3.17 by Daniel Schierbeck
Moved signature handling into signature widget.
433
        self.signature_table.set_revision(self._revision)
399.1.13 by Daniel Schierbeck
Merged with mainline.
434
450.6.4 by Daniel Schierbeck
Moved bug parsing code into the bug page itself.
435
    def _update_bugs(self, widget, param):
436
        self.bugs_page.set_revision(self._revision)
450.6.11 by Daniel Schierbeck
Made Bugs page always visible, but only sensitive when the revision has bug associations.
437
        label = self.get_tab_label(self.bugs_page)
461.1.1 by Daniel Schierbeck
Merged bug page improvements.
438
        label.set_sensitive(self.bugs_page.get_num_bugs() != 0)
450.6.4 by Daniel Schierbeck
Moved bug parsing code into the bug page itself.
439
412.1.13 by Daniel Schierbeck
Re-added support for displaying the children of a revision.
440
    def set_children(self, children):
441
        self._add_parents_or_children(children,
442
                                      self.children_widgets,
443
                                      self.children_table)
444
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
445
    def _show_clicked_cb(self, widget, revid, parentid):
446
        """Callback for when the show button for a parent is clicked."""
447
        self._show_callback(revid, parentid)
448
449
    def _go_clicked_cb(self, widget, revid):
450
        """Callback for when the go button for a parent is clicked."""
451
412.1.4 by Daniel Schierbeck
Made tag list smarter.
452
    def _add_tags(self, *args):
399.1.13 by Daniel Schierbeck
Merged with mainline.
453
        if self._revision is None:
454
            return
423.7.4 by Daniel Schierbeck
Made revisionview and branchview update when a tag is added.
455
412.1.4 by Daniel Schierbeck
Made tag list smarter.
456
        if self._tagdict.has_key(self._revision.revision_id):
457
            tags = self._tagdict[self._revision.revision_id]
458
        else:
459
            tags = []
460
            
241 by Jelmer Vernooij
Show tags in bzr viz.
461
        if tags == []:
462
            self.tags_list.hide()
463
            self.tags_label.hide()
464
            return
465
423.3.1 by Daniel Schierbeck
Made the tag list be a comma-separated line instead of a vertically stacked box.
466
        self.tags_list.set_text(", ".join(tags))
467
241 by Jelmer Vernooij
Show tags in bzr viz.
468
        self.tags_list.show_all()
469
        self.tags_label.show_all()
470
        
256.2.23 by Gary van der Merwe
Show Children
471
    def _add_parents_or_children(self, revids, widgets, table):
472
        while len(widgets) > 0:
473
            widget = widgets.pop()
474
            table.remove(widget)
475
        
476
        table.resize(max(len(revids), 1), 2)
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
477
256.2.23 by Gary van der Merwe
Show Children
478
        for idx, revid in enumerate(revids):
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
479
            align = gtk.Alignment(0.0, 0.0)
256.2.23 by Gary van der Merwe
Show Children
480
            widgets.append(align)
481
            table.attach(align, 1, 2, idx, idx + 1,
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
482
                                      gtk.EXPAND | gtk.FILL, gtk.FILL)
0.1.1 by Dan Loda
First working version of xannotate.
483
            align.show()
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
484
485
            hbox = gtk.HBox(False, spacing=6)
486
            align.add(hbox)
487
            hbox.show()
488
489
            image = gtk.Image()
490
            image.set_from_stock(
491
                gtk.STOCK_FIND, gtk.ICON_SIZE_SMALL_TOOLBAR)
492
            image.show()
493
148 by Jelmer Vernooij
Clean up interface a bit: don't show diff button when no diff can be accessed, use label instead of button when there is no callback set.
494
            if self._show_callback is not None:
495
                button = gtk.Button()
496
                button.add(image)
497
                button.connect("clicked", self._show_clicked_cb,
256.2.23 by Gary van der Merwe
Show Children
498
                               self._revision.revision_id, revid)
148 by Jelmer Vernooij
Clean up interface a bit: don't show diff button when no diff can be accessed, use label instead of button when there is no callback set.
499
                hbox.pack_start(button, expand=False, fill=True)
500
                button.show()
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
501
412.1.9 by Daniel Schierbeck
Removed the use of RevisionView.set_go_callback().
502
            button = gtk.Button(revid)
503
            button.connect("clicked",
412.1.14 by Daniel Schierbeck
Fixed bug in the way the child buttons worked.
504
                    lambda w, r: self.set_revision(self._branch.repository.get_revision(r)), revid)
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
505
            button.set_use_underline(False)
506
            hbox.pack_start(button, expand=False, fill=True)
507
            button.show()
0.1.1 by Dan Loda
First working version of xannotate.
508
324.2.1 by Daniel Schierbeck
Turned the logview into a notebook.
509
    def _create_general(self):
0.1.1 by Dan Loda
First working version of xannotate.
510
        vbox = gtk.VBox(False, 6)
511
        vbox.set_border_width(6)
512
        vbox.pack_start(self._create_headers(), expand=False, fill=True)
324.2.1 by Daniel Schierbeck
Turned the logview into a notebook.
513
        vbox.pack_start(self._create_message_view())
514
        self.append_page(vbox, tab_label=gtk.Label("General"))
515
        vbox.show()
516
517
    def _create_relations(self):
518
        vbox = gtk.VBox(False, 6)
519
        vbox.set_border_width(6)
291 by Jelmer Vernooij
Put children widget on a new line.
520
        vbox.pack_start(self._create_parents(), expand=False, fill=True)
412.1.13 by Daniel Schierbeck
Re-added support for displaying the children of a revision.
521
        vbox.pack_start(self._create_children(), expand=False, fill=True)
324.2.1 by Daniel Schierbeck
Turned the logview into a notebook.
522
        self.append_page(vbox, tab_label=gtk.Label("Relations"))
0.1.1 by Dan Loda
First working version of xannotate.
523
        vbox.show()
324.2.4 by Daniel Schierbeck
Added 'Changes' page to logview.
524
399.1.10 by Daniel Schierbeck
Improved implementation of the Signature page.
525
    def _create_signature(self):
399.3.17 by Daniel Schierbeck
Moved signature handling into signature widget.
526
        self.signature_table = SignatureTab(self._branch.repository)
399.1.15 by Jelmer Vernooij
Move signature tab to a separate class.
527
        self.append_page(self.signature_table, tab_label=gtk.Label('Signature'))
399.1.10 by Daniel Schierbeck
Improved implementation of the Signature page.
528
        self.connect_after('notify::revision', self._update_signature)
529
0.1.1 by Dan Loda
First working version of xannotate.
530
    def _create_headers(self):
241 by Jelmer Vernooij
Show tags in bzr viz.
531
        self.table = gtk.Table(rows=5, columns=2)
0.1.1 by Dan Loda
First working version of xannotate.
532
        self.table.set_row_spacings(6)
533
        self.table.set_col_spacings(6)
534
        self.table.show()
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
535
536
        align = gtk.Alignment(1.0, 0.5)
537
        label = gtk.Label()
538
        label.set_markup("<b>Revision Id:</b>")
539
        align.add(label)
540
        self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
541
        align.show()
542
        label.show()
543
544
        align = gtk.Alignment(0.0, 0.5)
412.1.5 by Daniel Schierbeck
Made the revision id label use signals when updating.
545
        revision_id = gtk.Label()
546
        revision_id.set_selectable(True)
547
        self.connect('notify::revision', 
548
                lambda w, p: revision_id.set_text(self._revision.revision_id))
549
        align.add(revision_id)
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
550
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
551
        align.show()
412.1.5 by Daniel Schierbeck
Made the revision id label use signals when updating.
552
        revision_id.show()
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
553
0.1.1 by Dan Loda
First working version of xannotate.
554
        align = gtk.Alignment(1.0, 0.5)
259 by Aaron Bentley
Add author support to gannotate and log viewer
555
        self.author_label = gtk.Label()
556
        self.author_label.set_markup("<b>Author:</b>")
557
        align.add(self.author_label)
558
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
559
        align.show()
560
        self.author_label.show()
561
562
        align = gtk.Alignment(0.0, 0.5)
563
        self.author = gtk.Label()
564
        self.author.set_selectable(True)
565
        align.add(self.author)
566
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
567
        align.show()
568
        self.author.show()
569
        self.author.hide()
570
571
        align = gtk.Alignment(1.0, 0.5)
0.1.1 by Dan Loda
First working version of xannotate.
572
        label = gtk.Label()
573
        label.set_markup("<b>Committer:</b>")
574
        align.add(label)
259 by Aaron Bentley
Add author support to gannotate and log viewer
575
        self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
0.1.1 by Dan Loda
First working version of xannotate.
576
        align.show()
577
        label.show()
578
579
        align = gtk.Alignment(0.0, 0.5)
580
        self.committer = gtk.Label()
581
        self.committer.set_selectable(True)
582
        align.add(self.committer)
259 by Aaron Bentley
Add author support to gannotate and log viewer
583
        self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
0.1.1 by Dan Loda
First working version of xannotate.
584
        align.show()
585
        self.committer.show()
586
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
587
        align = gtk.Alignment(0.0, 0.5)
588
        label = gtk.Label()
589
        label.set_markup("<b>Branch nick:</b>")
590
        align.add(label)
259 by Aaron Bentley
Add author support to gannotate and log viewer
591
        self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
592
        label.show()
593
        align.show()
594
595
        align = gtk.Alignment(0.0, 0.5)
596
        self.branchnick_label = gtk.Label()
597
        self.branchnick_label.set_selectable(True)
598
        align.add(self.branchnick_label)
259 by Aaron Bentley
Add author support to gannotate and log viewer
599
        self.table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
600
        self.branchnick_label.show()
601
        align.show()
602
0.1.1 by Dan Loda
First working version of xannotate.
603
        align = gtk.Alignment(1.0, 0.5)
604
        label = gtk.Label()
605
        label.set_markup("<b>Timestamp:</b>")
606
        align.add(label)
259 by Aaron Bentley
Add author support to gannotate and log viewer
607
        self.table.attach(align, 0, 1, 4, 5, gtk.FILL, gtk.FILL)
0.1.1 by Dan Loda
First working version of xannotate.
608
        align.show()
609
        label.show()
610
611
        align = gtk.Alignment(0.0, 0.5)
612
        self.timestamp = gtk.Label()
613
        self.timestamp.set_selectable(True)
614
        align.add(self.timestamp)
259 by Aaron Bentley
Add author support to gannotate and log viewer
615
        self.table.attach(align, 1, 2, 4, 5, gtk.EXPAND | gtk.FILL, gtk.FILL)
0.1.1 by Dan Loda
First working version of xannotate.
616
        align.show()
617
        self.timestamp.show()
618
241 by Jelmer Vernooij
Show tags in bzr viz.
619
        align = gtk.Alignment(1.0, 0.5)
620
        self.tags_label = gtk.Label()
621
        self.tags_label.set_markup("<b>Tags:</b>")
622
        align.add(self.tags_label)
623
        align.show()
261 by Aaron Bentley
Fix tags formatting
624
        self.table.attach(align, 0, 1, 5, 6, gtk.FILL, gtk.FILL)
241 by Jelmer Vernooij
Show tags in bzr viz.
625
        self.tags_label.show()
626
627
        align = gtk.Alignment(0.0, 0.5)
423.3.1 by Daniel Schierbeck
Made the tag list be a comma-separated line instead of a vertically stacked box.
628
        self.tags_list = gtk.Label()
241 by Jelmer Vernooij
Show tags in bzr viz.
629
        align.add(self.tags_list)
261 by Aaron Bentley
Fix tags formatting
630
        self.table.attach(align, 1, 2, 5, 6, gtk.EXPAND | gtk.FILL, gtk.FILL)
241 by Jelmer Vernooij
Show tags in bzr viz.
631
        align.show()
632
        self.tags_list.show()
633
412.1.4 by Daniel Schierbeck
Made tag list smarter.
634
        self.connect('notify::revision', self._add_tags)
635
0.1.1 by Dan Loda
First working version of xannotate.
636
        return self.table
256.2.23 by Gary van der Merwe
Show Children
637
    
291 by Jelmer Vernooij
Put children widget on a new line.
638
    def _create_parents(self):
639
        hbox = gtk.HBox(True, 3)
256.2.23 by Gary van der Merwe
Show Children
640
        
641
        self.parents_table = self._create_parents_or_children_table(
642
            "<b>Parents:</b>")
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
643
        self.parents_widgets = []
256.2.23 by Gary van der Merwe
Show Children
644
        hbox.pack_start(self.parents_table)
291 by Jelmer Vernooij
Put children widget on a new line.
645
646
        hbox.show()
647
        return hbox
648
649
    def _create_children(self):
650
        hbox = gtk.HBox(True, 3)
651
        self.children_table = self._create_parents_or_children_table(
652
            "<b>Children:</b>")
653
        self.children_widgets = []
654
        hbox.pack_start(self.children_table)
256.2.23 by Gary van der Merwe
Show Children
655
        hbox.show()
656
        return hbox
657
        
658
    def _create_parents_or_children_table(self, text):
659
        table = gtk.Table(rows=1, columns=2)
660
        table.set_row_spacings(3)
661
        table.set_col_spacings(6)
662
        table.show()
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
663
664
        label = gtk.Label()
256.2.23 by Gary van der Merwe
Show Children
665
        label.set_markup(text)
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
666
        align = gtk.Alignment(0.0, 0.5)
667
        align.add(label)
256.2.23 by Gary van der Merwe
Show Children
668
        table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
669
        label.show()
670
        align.show()
671
256.2.23 by Gary van der Merwe
Show Children
672
        return table
147 by Jelmer Vernooij
Remove a bunch of duplicate functionality.
673
0.1.1 by Dan Loda
First working version of xannotate.
674
    def _create_message_view(self):
412.1.6 by Daniel Schierbeck
Made the message buffer use signals when updating.
675
        msg_buffer = gtk.TextBuffer()
676
        self.connect('notify::revision',
677
                lambda w, p: msg_buffer.set_text(self._revision.message))
324.2.2 by Daniel Schierbeck
Surrounded the commit message textview with a scrolled window and added a shadow.
678
        window = gtk.ScrolledWindow()
679
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
680
        window.set_shadow_type(gtk.SHADOW_IN)
412.1.6 by Daniel Schierbeck
Made the message buffer use signals when updating.
681
        tv = gtk.TextView(msg_buffer)
0.1.1 by Dan Loda
First working version of xannotate.
682
        tv.set_editable(False)
683
        tv.set_wrap_mode(gtk.WRAP_WORD)
399.1.13 by Daniel Schierbeck
Merged with mainline.
684
0.1.1 by Dan Loda
First working version of xannotate.
685
        tv.modify_font(pango.FontDescription("Monospace"))
686
        tv.show()
324.2.2 by Daniel Schierbeck
Surrounded the commit message textview with a scrolled window and added a shadow.
687
        window.add(tv)
688
        window.show()
689
        return window
0.1.1 by Dan Loda
First working version of xannotate.
690
423.14.1 by Jelmer Vernooij
Add bugs tab to display bug status change metadata.
691
    def _create_bugs(self):
450.6.4 by Daniel Schierbeck
Moved bug parsing code into the bug page itself.
692
        self.bugs_page = BugsTab()
693
        self.connect_after('notify::revision', self._update_bugs) 
694
        self.append_page(self.bugs_page, tab_label=gtk.Label('Bugs'))
423.14.1 by Jelmer Vernooij
Add bugs tab to display bug status change metadata.
695
278.1.2 by John Arbash Meinel
Add an extra box that pops up when we have per-file information.
696
    def _create_file_info_view(self):
278.1.45 by John Arbash Meinel
Switch to a new tab for per-file messages.
697
        self.file_info_box = gtk.VBox(False, 6)
698
        self.file_info_box.set_border_width(6)
278.1.2 by John Arbash Meinel
Add an extra box that pops up when we have per-file information.
699
        self.file_info_buffer = gtk.TextBuffer()
278.1.44 by John Arbash Meinel
Merge in trunk, and update logview per-file commit messages.
700
        window = gtk.ScrolledWindow()
701
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
702
        window.set_shadow_type(gtk.SHADOW_IN)
278.1.2 by John Arbash Meinel
Add an extra box that pops up when we have per-file information.
703
        tv = gtk.TextView(self.file_info_buffer)
704
        tv.set_editable(False)
705
        tv.set_wrap_mode(gtk.WRAP_WORD)
706
        tv.modify_font(pango.FontDescription("Monospace"))
707
        tv.show()
278.1.44 by John Arbash Meinel
Merge in trunk, and update logview per-file commit messages.
708
        window.add(tv)
709
        window.show()
710
        self.file_info_box.pack_start(window)
278.1.2 by John Arbash Meinel
Add an extra box that pops up when we have per-file information.
711
        self.file_info_box.hide() # Only shown when there are per-file messages
278.1.45 by John Arbash Meinel
Switch to a new tab for per-file messages.
712
        self.append_page(self.file_info_box, tab_label=gtk.Label('Per-file'))
278.1.2 by John Arbash Meinel
Add an extra box that pops up when we have per-file information.
713