/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 subprocess
24
21
 
25
22
from bzrlib.osutils import format_date
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):
 
23
 
 
24
 
 
25
class LogView(gtk.ScrolledWindow):
58
26
    """ Custom widget for commit log details.
59
27
 
60
28
    A variety of bzr tools may need to implement such a thing. This is a
61
29
    start.
62
30
    """
63
31
 
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)
 
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)
149
41
 
150
42
    def set_revision(self, revision):
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 = []
 
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)
244
53
            
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)
 
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)
267
60
            align.show()
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):
 
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):
294
84
        vbox = gtk.VBox(False, 6)
295
85
        vbox.set_border_width(6)
296
86
        vbox.pack_start(self._create_headers(), expand=False, fill=True)
297
87
        vbox.pack_start(self._create_message_view())
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"))
 
88
        self.add_with_viewport(vbox)
307
89
        vbox.show()
308
90
 
309
91
    def _create_headers(self):
310
 
        self.table = gtk.Table(rows=5, columns=2)
 
92
        self.table = gtk.Table(rows=4, columns=2)
311
93
        self.table.set_row_spacings(6)
312
94
        self.table.set_col_spacings(6)
313
95
        self.table.show()
314
 
 
 
96
        
315
97
        align = gtk.Alignment(1.0, 0.5)
316
98
        label = gtk.Label()
317
 
        label.set_markup("<b>Revision Id:</b>")
 
99
        label.set_markup("<b>Committer:</b>")
318
100
        align.add(label)
319
101
        self.table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
320
102
        align.show()
321
103
        label.show()
322
104
 
323
105
        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
 
 
350
 
        align = gtk.Alignment(1.0, 0.5)
351
 
        label = gtk.Label()
352
 
        label.set_markup("<b>Committer:</b>")
353
 
        align.add(label)
354
 
        self.table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
355
 
        align.show()
356
 
        label.show()
357
 
 
358
 
        align = gtk.Alignment(0.0, 0.5)
359
106
        self.committer = gtk.Label()
360
107
        self.committer.set_selectable(True)
361
108
        align.add(self.committer)
362
 
        self.table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
109
        self.table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
363
110
        align.show()
364
111
        self.committer.show()
365
112
 
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
 
 
382
113
        align = gtk.Alignment(1.0, 0.5)
383
114
        label = gtk.Label()
384
115
        label.set_markup("<b>Timestamp:</b>")
385
116
        align.add(label)
386
 
        self.table.attach(align, 0, 1, 4, 5, gtk.FILL, gtk.FILL)
 
117
        self.table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
387
118
        align.show()
388
119
        label.show()
389
120
 
391
122
        self.timestamp = gtk.Label()
392
123
        self.timestamp.set_selectable(True)
393
124
        align.add(self.timestamp)
394
 
        self.table.attach(align, 1, 2, 4, 5, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
125
        self.table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
395
126
        align.show()
396
127
        self.timestamp.show()
397
128
 
398
129
        align = gtk.Alignment(1.0, 0.5)
399
 
        self.tags_label = gtk.Label()
400
 
        self.tags_label.set_markup("<b>Tags:</b>")
401
 
        align.add(self.tags_label)
 
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)
402
134
        align.show()
403
 
        self.table.attach(align, 0, 1, 5, 6, gtk.FILL, gtk.FILL)
404
 
        self.tags_label.show()
 
135
        label.show()
405
136
 
406
137
        align = gtk.Alignment(0.0, 0.5)
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)
 
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)
410
142
        align.show()
411
 
        self.tags_list.show()
412
 
 
413
 
        self.connect('notify::revision', self._add_tags)
 
143
        self.revision_id.show()
414
144
 
415
145
        return self.table
416
146
 
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
 
 
454
147
    def _create_message_view(self):
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'))
 
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
491
155