/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-03-14 02:13:27 UTC
  • mto: (452.2.2 trunk)
  • mto: This revision was merged to the branch mainline in revision 453.
  • Revision ID: jelmer@samba.org-20080314021327-q1pabtpneeasz8qv
Fix support for default value in BranchSelectionDialog.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005 Dan Loda <danloda@gmail.com>
 
2
# Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org>
2
3
 
3
4
# This program is free software; you can redistribute it and/or modify
4
5
# it under the terms of the GNU General Public License as published by
18
19
pygtk.require("2.0")
19
20
import gtk
20
21
import pango
 
22
import gobject
 
23
import subprocess
21
24
 
22
25
from bzrlib.osutils import format_date
23
 
 
24
 
 
25
 
class LogView(gtk.ScrolledWindow):
 
26
from bzrlib.util.bencode import bdecode
 
27
 
 
28
def _open_link(widget, uri):
 
29
    subprocess.Popen(['sensible-browser', uri], close_fds=True)
 
30
 
 
31
gtk.link_button_set_uri_hook(_open_link)
 
32
 
 
33
class BugsTab(gtk.Table):
 
34
    def __init__(self):
 
35
        super(BugsTab, self).__init__(rows=5, columns=2)
 
36
        self.set_row_spacings(6)
 
37
        self.set_col_spacings(6)
 
38
        self.clear()
 
39
 
 
40
    def clear(self):
 
41
        for c in self.get_children():
 
42
            self.remove(c)
 
43
        self.count = 0
 
44
        self.hide_all() # Only shown when there are bugs
 
45
 
 
46
    def add_bug(self, url, status):
 
47
        button = gtk.LinkButton(url, url)
 
48
        self.attach(button, 0, 1, self.count, self.count + 1,
 
49
                              gtk.EXPAND | gtk.FILL, gtk.FILL)
 
50
        status_label = gtk.Label(status)
 
51
        self.attach(status_label, 1, 2, self.count, self.count + 1,
 
52
                              gtk.EXPAND | gtk.FILL, gtk.FILL)
 
53
        self.count += 1
 
54
        self.show_all()
 
55
 
 
56
 
 
57
class RevisionView(gtk.Notebook):
26
58
    """ Custom widget for commit log details.
27
59
 
28
60
    A variety of bzr tools may need to implement such a thing. This is a
29
61
    start.
30
62
    """
31
63
 
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)
 
64
    __gproperties__ = {
 
65
        'branch': (
 
66
            gobject.TYPE_PYOBJECT,
 
67
            'Branch',
 
68
            'The branch holding the revision being displayed',
 
69
            gobject.PARAM_CONSTRUCT_ONLY | gobject.PARAM_WRITABLE
 
70
        ),
 
71
 
 
72
        'revision': (
 
73
            gobject.TYPE_PYOBJECT,
 
74
            'Revision',
 
75
            'The revision being displayed',
 
76
            gobject.PARAM_READWRITE
 
77
        ),
 
78
 
 
79
        'children': (
 
80
            gobject.TYPE_PYOBJECT,
 
81
            'Children',
 
82
            'Child revisions',
 
83
            gobject.PARAM_READWRITE
 
84
        ),
 
85
 
 
86
        'file-id': (
 
87
            gobject.TYPE_PYOBJECT,
 
88
            'File Id',
 
89
            'The file id',
 
90
            gobject.PARAM_READWRITE
 
91
        )
 
92
    }
 
93
 
 
94
 
 
95
    def __init__(self, branch=None):
 
96
        gtk.Notebook.__init__(self)
 
97
 
 
98
        self._create_general()
 
99
        self._create_relations()
 
100
        self._create_file_info_view()
 
101
        self._create_bugs()
 
102
 
 
103
        self.set_current_page(0)
 
104
        
 
105
        self._show_callback = None
 
106
        self._clicked_callback = None
 
107
 
 
108
        self._revision = None
 
109
        self._branch = branch
 
110
 
 
111
        self.update_tags()
 
112
 
 
113
        self.set_file_id(None)
 
114
 
 
115
    def do_get_property(self, property):
 
116
        if property.name == 'branch':
 
117
            return self._branch
 
118
        elif property.name == 'revision':
 
119
            return self._revision
 
120
        elif property.name == 'children':
 
121
            return self._children
 
122
        elif property.name == 'file-id':
 
123
            return self._file_id
 
124
        else:
 
125
            raise AttributeError, 'unknown property %s' % property.name
 
126
 
 
127
    def do_set_property(self, property, value):
 
128
        if property.name == 'branch':
 
129
            self._branch = value
 
130
        elif property.name == 'revision':
 
131
            self._set_revision(value)
 
132
        elif property.name == 'children':
 
133
            self.set_children(value)
 
134
        elif property.name == 'file-id':
 
135
            self._file_id = value
 
136
        else:
 
137
            raise AttributeError, 'unknown property %s' % property.name
 
138
 
 
139
    def set_show_callback(self, callback):
 
140
        self._show_callback = callback
 
141
 
 
142
    def set_file_id(self, file_id):
 
143
        """Set a specific file id that we want to track.
 
144
 
 
145
        This just effects the display of a per-file commit message.
 
146
        If it is set to None, then all commit messages will be shown.
 
147
        """
 
148
        self.set_property('file-id', file_id)
41
149
 
42
150
    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)
 
151
        if revision != self._revision:
 
152
            self.set_property('revision', revision)
 
153
 
 
154
    def get_revision(self):
 
155
        return self.get_property('revision')
 
156
 
 
157
    def _set_revision(self, revision):
 
158
        if revision is None: return
 
159
 
 
160
        self._revision = revision
 
161
        if revision.committer is not None:
 
162
            self.committer.set_text(revision.committer)
 
163
        else:
 
164
            self.committer.set_text("")
 
165
        author = revision.properties.get('author', '')
 
166
        if author != '':
 
167
            self.author.set_text(author)
 
168
            self.author.show()
 
169
            self.author_label.show()
 
170
        else:
 
171
            self.author.hide()
 
172
            self.author_label.hide()
 
173
 
 
174
        if revision.timestamp is not None:
 
175
            self.timestamp.set_text(format_date(revision.timestamp,
 
176
                                                revision.timezone))
 
177
        try:
 
178
            self.branchnick_label.set_text(revision.properties['branch-nick'])
 
179
        except KeyError:
 
180
            self.branchnick_label.set_text("")
 
181
 
 
182
        self._add_parents_or_children(revision.parent_ids,
 
183
                                      self.parents_widgets,
 
184
                                      self.parents_table)
 
185
        
 
186
        file_info = revision.properties.get('file-info', None)
 
187
        if file_info is not None:
 
188
            file_info = bdecode(file_info.encode('UTF-8'))
 
189
 
 
190
        if file_info:
 
191
            if self._file_id is None:
 
192
                text = []
 
193
                for fi in file_info:
 
194
                    text.append('%(path)s\n%(message)s' % fi)
 
195
                self.file_info_buffer.set_text('\n'.join(text))
 
196
                self.file_info_box.show()
 
197
            else:
 
198
                text = []
 
199
                for fi in file_info:
 
200
                    if fi['file_id'] == self._file_id:
 
201
                        text.append(fi['message'])
 
202
                if text:
 
203
                    self.file_info_buffer.set_text('\n'.join(text))
 
204
                    self.file_info_box.show()
 
205
                else:
 
206
                    self.file_info_box.hide()
 
207
        else:
 
208
            self.file_info_box.hide()
 
209
 
 
210
        self.bugs_table.clear()
 
211
        bugs_text = revision.properties.get('bugs', None)
 
212
        if bugs_text:
 
213
            for bugline in bugs_text.splitlines():
 
214
                (url, status) = bugline.split(" ")
 
215
                self.bugs_table.add_bug(url, status)
 
216
 
 
217
    def update_tags(self):
 
218
        if self._branch is not None and self._branch.supports_tags():
 
219
            self._tagdict = self._branch.tags.get_reverse_tag_dict()
 
220
        else:
 
221
            self._tagdict = {}
 
222
 
 
223
        self._add_tags()
 
224
 
 
225
    def set_children(self, children):
 
226
        self._add_parents_or_children(children,
 
227
                                      self.children_widgets,
 
228
                                      self.children_table)
 
229
 
 
230
    def _show_clicked_cb(self, widget, revid, parentid):
 
231
        """Callback for when the show button for a parent is clicked."""
 
232
        self._show_callback(revid, parentid)
 
233
 
 
234
    def _go_clicked_cb(self, widget, revid):
 
235
        """Callback for when the go button for a parent is clicked."""
 
236
 
 
237
    def _add_tags(self, *args):
 
238
        if self._revision is None: return
 
239
 
 
240
        if self._tagdict.has_key(self._revision.revision_id):
 
241
            tags = self._tagdict[self._revision.revision_id]
 
242
        else:
 
243
            tags = []
53
244
            
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)
 
245
        if tags == []:
 
246
            self.tags_list.hide()
 
247
            self.tags_label.hide()
 
248
            return
 
249
 
 
250
        self.tags_list.set_text(", ".join(tags))
 
251
 
 
252
        self.tags_list.show_all()
 
253
        self.tags_label.show_all()
 
254
        
 
255
    def _add_parents_or_children(self, revids, widgets, table):
 
256
        while len(widgets) > 0:
 
257
            widget = widgets.pop()
 
258
            table.remove(widget)
 
259
        
 
260
        table.resize(max(len(revids), 1), 2)
 
261
 
 
262
        for idx, revid in enumerate(revids):
 
263
            align = gtk.Alignment(0.0, 0.0)
 
264
            widgets.append(align)
 
265
            table.attach(align, 1, 2, idx, idx + 1,
 
266
                                      gtk.EXPAND | gtk.FILL, gtk.FILL)
60
267
            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):
 
268
 
 
269
            hbox = gtk.HBox(False, spacing=6)
 
270
            align.add(hbox)
 
271
            hbox.show()
 
272
 
 
273
            image = gtk.Image()
 
274
            image.set_from_stock(
 
275
                gtk.STOCK_FIND, gtk.ICON_SIZE_SMALL_TOOLBAR)
 
276
            image.show()
 
277
 
 
278
            if self._show_callback is not None:
 
279
                button = gtk.Button()
 
280
                button.add(image)
 
281
                button.connect("clicked", self._show_clicked_cb,
 
282
                               self._revision.revision_id, revid)
 
283
                hbox.pack_start(button, expand=False, fill=True)
 
284
                button.show()
 
285
 
 
286
            button = gtk.Button(revid)
 
287
            button.connect("clicked",
 
288
                    lambda w, r: self.set_revision(self._branch.repository.get_revision(r)), revid)
 
289
            button.set_use_underline(False)
 
290
            hbox.pack_start(button, expand=False, fill=True)
 
291
            button.show()
 
292
 
 
293
    def _create_general(self):
84
294
        vbox = gtk.VBox(False, 6)
85
295
        vbox.set_border_width(6)
86
296
        vbox.pack_start(self._create_headers(), expand=False, fill=True)
87
297
        vbox.pack_start(self._create_message_view())
88
 
        self.add_with_viewport(vbox)
 
298
        self.append_page(vbox, tab_label=gtk.Label("General"))
 
299
        vbox.show()
 
300
 
 
301
    def _create_relations(self):
 
302
        vbox = gtk.VBox(False, 6)
 
303
        vbox.set_border_width(6)
 
304
        vbox.pack_start(self._create_parents(), expand=False, fill=True)
 
305
        vbox.pack_start(self._create_children(), expand=False, fill=True)
 
306
        self.append_page(vbox, tab_label=gtk.Label("Relations"))
89
307
        vbox.show()
90
308
 
91
309
    def _create_headers(self):
92
 
        self.table = gtk.Table(rows=4, columns=2)
 
310
        self.table = gtk.Table(rows=5, columns=2)
93
311
        self.table.set_row_spacings(6)
94
312
        self.table.set_col_spacings(6)
95
313
        self.table.show()
96
 
        
 
314
 
 
315
        align = gtk.Alignment(1.0, 0.5)
 
316
        label = gtk.Label()
 
317
        label.set_markup("<b>Revision Id:</b>")
 
318
        align.add(label)
 
319
        self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
320
        align.show()
 
321
        label.show()
 
322
 
 
323
        align = gtk.Alignment(0.0, 0.5)
 
324
        revision_id = gtk.Label()
 
325
        revision_id.set_selectable(True)
 
326
        self.connect('notify::revision', 
 
327
                lambda w, p: revision_id.set_text(self._revision.revision_id))
 
328
        align.add(revision_id)
 
329
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
330
        align.show()
 
331
        revision_id.show()
 
332
 
 
333
        align = gtk.Alignment(1.0, 0.5)
 
334
        self.author_label = gtk.Label()
 
335
        self.author_label.set_markup("<b>Author:</b>")
 
336
        align.add(self.author_label)
 
337
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
338
        align.show()
 
339
        self.author_label.show()
 
340
 
 
341
        align = gtk.Alignment(0.0, 0.5)
 
342
        self.author = gtk.Label()
 
343
        self.author.set_selectable(True)
 
344
        align.add(self.author)
 
345
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
346
        align.show()
 
347
        self.author.show()
 
348
        self.author.hide()
 
349
 
97
350
        align = gtk.Alignment(1.0, 0.5)
98
351
        label = gtk.Label()
99
352
        label.set_markup("<b>Committer:</b>")
100
353
        align.add(label)
101
 
        self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
354
        self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
102
355
        align.show()
103
356
        label.show()
104
357
 
106
359
        self.committer = gtk.Label()
107
360
        self.committer.set_selectable(True)
108
361
        align.add(self.committer)
109
 
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
362
        self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
110
363
        align.show()
111
364
        self.committer.show()
112
365
 
 
366
        align = gtk.Alignment(0.0, 0.5)
 
367
        label = gtk.Label()
 
368
        label.set_markup("<b>Branch nick:</b>")
 
369
        align.add(label)
 
370
        self.table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
 
371
        label.show()
 
372
        align.show()
 
373
 
 
374
        align = gtk.Alignment(0.0, 0.5)
 
375
        self.branchnick_label = gtk.Label()
 
376
        self.branchnick_label.set_selectable(True)
 
377
        align.add(self.branchnick_label)
 
378
        self.table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
379
        self.branchnick_label.show()
 
380
        align.show()
 
381
 
113
382
        align = gtk.Alignment(1.0, 0.5)
114
383
        label = gtk.Label()
115
384
        label.set_markup("<b>Timestamp:</b>")
116
385
        align.add(label)
117
 
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
386
        self.table.attach(align, 0, 1, 4, 5, gtk.FILL, gtk.FILL)
118
387
        align.show()
119
388
        label.show()
120
389
 
122
391
        self.timestamp = gtk.Label()
123
392
        self.timestamp.set_selectable(True)
124
393
        align.add(self.timestamp)
125
 
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
394
        self.table.attach(align, 1, 2, 4, 5, gtk.EXPAND | gtk.FILL, gtk.FILL)
126
395
        align.show()
127
396
        self.timestamp.show()
128
397
 
129
398
        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)
 
399
        self.tags_label = gtk.Label()
 
400
        self.tags_label.set_markup("<b>Tags:</b>")
 
401
        align.add(self.tags_label)
134
402
        align.show()
135
 
        label.show()
 
403
        self.table.attach(align, 0, 1, 5, 6, gtk.FILL, gtk.FILL)
 
404
        self.tags_label.show()
136
405
 
137
406
        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)
 
407
        self.tags_list = gtk.Label()
 
408
        align.add(self.tags_list)
 
409
        self.table.attach(align, 1, 2, 5, 6, gtk.EXPAND | gtk.FILL, gtk.FILL)
142
410
        align.show()
143
 
        self.revision_id.show()
 
411
        self.tags_list.show()
 
412
 
 
413
        self.connect('notify::revision', self._add_tags)
144
414
 
145
415
        return self.table
146
416
 
 
417
    
 
418
    def _create_parents(self):
 
419
        hbox = gtk.HBox(True, 3)
 
420
        
 
421
        self.parents_table = self._create_parents_or_children_table(
 
422
            "<b>Parents:</b>")
 
423
        self.parents_widgets = []
 
424
        hbox.pack_start(self.parents_table)
 
425
 
 
426
        hbox.show()
 
427
        return hbox
 
428
 
 
429
    def _create_children(self):
 
430
        hbox = gtk.HBox(True, 3)
 
431
        self.children_table = self._create_parents_or_children_table(
 
432
            "<b>Children:</b>")
 
433
        self.children_widgets = []
 
434
        hbox.pack_start(self.children_table)
 
435
        hbox.show()
 
436
        return hbox
 
437
        
 
438
    def _create_parents_or_children_table(self, text):
 
439
        table = gtk.Table(rows=1, columns=2)
 
440
        table.set_row_spacings(3)
 
441
        table.set_col_spacings(6)
 
442
        table.show()
 
443
 
 
444
        label = gtk.Label()
 
445
        label.set_markup(text)
 
446
        align = gtk.Alignment(0.0, 0.5)
 
447
        align.add(label)
 
448
        table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
449
        label.show()
 
450
        align.show()
 
451
 
 
452
        return table
 
453
 
147
454
    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
 
455
        msg_buffer = gtk.TextBuffer()
 
456
        self.connect('notify::revision',
 
457
                lambda w, p: msg_buffer.set_text(self._revision.message))
 
458
        window = gtk.ScrolledWindow()
 
459
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
460
        window.set_shadow_type(gtk.SHADOW_IN)
 
461
        tv = gtk.TextView(msg_buffer)
 
462
        tv.set_editable(False)
 
463
        tv.set_wrap_mode(gtk.WRAP_WORD)
 
464
        tv.modify_font(pango.FontDescription("Monospace"))
 
465
        tv.show()
 
466
        window.add(tv)
 
467
        window.show()
 
468
        return window
 
469
 
 
470
    def _create_bugs(self):
 
471
        self.bugs_table = BugsTab()
 
472
        self.append_page(self.bugs_table, tab_label=gtk.Label('Bugs'))
 
473
 
 
474
    def _create_file_info_view(self):
 
475
        self.file_info_box = gtk.VBox(False, 6)
 
476
        self.file_info_box.set_border_width(6)
 
477
        self.file_info_buffer = gtk.TextBuffer()
 
478
        window = gtk.ScrolledWindow()
 
479
        window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
480
        window.set_shadow_type(gtk.SHADOW_IN)
 
481
        tv = gtk.TextView(self.file_info_buffer)
 
482
        tv.set_editable(False)
 
483
        tv.set_wrap_mode(gtk.WRAP_WORD)
 
484
        tv.modify_font(pango.FontDescription("Monospace"))
 
485
        tv.show()
 
486
        window.add(tv)
 
487
        window.show()
 
488
        self.file_info_box.pack_start(window)
 
489
        self.file_info_box.hide() # Only shown when there are per-file messages
 
490
        self.append_page(self.file_info_box, tab_label=gtk.Label('Per-file'))
155
491