/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 olive/frontend/gtk/viz/branchwin.py

  • Committer: Szilveszter Farkas (Phanatic)
  • Date: 2006-09-27 19:11:59 UTC
  • mfrom: (0.8.90 merge)
  • mto: (93.1.1 win32.bialix)
  • mto: This revision was merged to the branch mainline in revision 103.
  • Revision ID: Szilveszter.Farkas@gmail.com-20060927191159-cc4e54f613575779
Merge all changes. Release 0.11.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Modified for use with Olive:
2
 
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
3
 
# Original copyright holder:
4
 
# Copyright (C) 2005 by Canonical Ltd. (Scott James Remnant <scott@ubuntu.com>)
5
 
#
6
 
# This program is free software; you can redistribute it and/or modify
7
 
# it under the terms of the GNU General Public License as published by
8
 
# the Free Software Foundation; either version 2 of the License, or
9
 
# (at your option) any later version.
10
 
#
11
 
# This program is distributed in the hope that it will be useful,
12
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 
# GNU General Public License for more details.
15
 
#
16
 
# You should have received a copy of the GNU General Public License
17
 
# along with this program; if not, write to the Free Software
18
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 
 
20
 
"""Branch window.
21
 
 
22
 
This module contains the code to manage the branch information window,
23
 
which contains both the revision graph and details panes.
24
 
"""
25
 
 
26
 
__copyright__ = "Copyright © 2005 Canonical Ltd."
27
 
__author__    = "Scott James Remnant <scott@ubuntu.com>"
28
 
 
29
 
 
30
 
import gtk
31
 
import gobject
32
 
import pango
33
 
 
34
 
from bzrlib.osutils import format_date
35
 
 
36
 
from graph import distances, graph, same_branch
37
 
from graphcell import CellRendererGraph
38
 
 
39
 
 
40
 
class BranchWindow(gtk.Window):
41
 
    """Branch window.
42
 
 
43
 
    This object represents and manages a single window containing information
44
 
    for a particular branch.
45
 
    """
46
 
 
47
 
    def __init__(self, app=None):
48
 
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
49
 
        self.set_border_width(0)
50
 
        self.set_title("bzrk")
51
 
 
52
 
        self.app = app
53
 
 
54
 
        # Use three-quarters of the screen by default
55
 
        screen = self.get_screen()
56
 
        monitor = screen.get_monitor_geometry(0)
57
 
        width = int(monitor.width * 0.75)
58
 
        height = int(monitor.height * 0.75)
59
 
        self.set_default_size(width, height)
60
 
 
61
 
        # FIXME AndyFitz!
62
 
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
63
 
        self.set_icon(icon)
64
 
 
65
 
        self.accel_group = gtk.AccelGroup()
66
 
        self.add_accel_group(self.accel_group)
67
 
 
68
 
        self.construct()
69
 
 
70
 
    def construct(self):
71
 
        """Construct the window contents."""
72
 
        vbox = gtk.VBox(spacing=0)
73
 
        self.add(vbox)
74
 
 
75
 
        vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
76
 
 
77
 
        paned = gtk.VPaned()
78
 
        paned.pack1(self.construct_top(), resize=True, shrink=False)
79
 
        paned.pack2(self.construct_bottom(), resize=False, shrink=True)
80
 
        paned.show()
81
 
        vbox.pack_start(paned, expand=True, fill=True)
82
 
        vbox.set_focus_child(paned)
83
 
 
84
 
        vbox.show()
85
 
 
86
 
    def construct_top(self):
87
 
        """Construct the top-half of the window."""
88
 
        scrollwin = gtk.ScrolledWindow()
89
 
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
90
 
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
91
 
        scrollwin.show()
92
 
 
93
 
        self.treeview = gtk.TreeView()
94
 
        self.treeview.set_rules_hint(True)
95
 
        self.treeview.set_search_column(4)
96
 
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
97
 
        self.treeview.connect("row-activated", self._treeview_row_activated_cb)
98
 
        scrollwin.add(self.treeview)
99
 
        self.treeview.show()
100
 
 
101
 
        cell = CellRendererGraph()
102
 
        column = gtk.TreeViewColumn()
103
 
        column.set_resizable(True)
104
 
        column.pack_start(cell, expand=False)
105
 
        column.add_attribute(cell, "node", 1)
106
 
        column.add_attribute(cell, "in-lines", 2)
107
 
        column.add_attribute(cell, "out-lines", 3)
108
 
        self.treeview.append_column(column)
109
 
 
110
 
        cell = gtk.CellRendererText()
111
 
        cell.set_property("width-chars", 40)
112
 
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
113
 
        column = gtk.TreeViewColumn(_('Message'))
114
 
        column.set_resizable(True)
115
 
        column.pack_start(cell, expand=True)
116
 
        column.add_attribute(cell, "text", 4)
117
 
        self.treeview.append_column(column)
118
 
 
119
 
        cell = gtk.CellRendererText()
120
 
        cell.set_property("width-chars", 40)
121
 
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
122
 
        column = gtk.TreeViewColumn(_('Committer'))
123
 
        column.set_resizable(True)
124
 
        column.pack_start(cell, expand=True)
125
 
        column.add_attribute(cell, "text", 5)
126
 
        self.treeview.append_column(column)
127
 
 
128
 
        cell = gtk.CellRendererText()
129
 
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
130
 
        column = gtk.TreeViewColumn(_('Date'))
131
 
        column.set_resizable(True)
132
 
        column.pack_start(cell, expand=True)
133
 
        column.add_attribute(cell, "text", 6)
134
 
        self.treeview.append_column(column)
135
 
 
136
 
        return scrollwin
137
 
 
138
 
    def construct_navigation(self):
139
 
        """Construct the navigation buttons."""
140
 
        frame = gtk.Frame()
141
 
        frame.set_shadow_type(gtk.SHADOW_OUT)
142
 
        frame.show()
143
 
        
144
 
        hbox = gtk.HBox(spacing=12)
145
 
        frame.add(hbox)
146
 
        hbox.show()
147
 
 
148
 
        self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
149
 
        self.back_button.set_relief(gtk.RELIEF_NONE)
150
 
        self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
151
 
                                         0, 0)
152
 
        self.back_button.connect("clicked", self._back_clicked_cb)
153
 
        hbox.pack_start(self.back_button, expand=False, fill=True)
154
 
        self.back_button.show()
155
 
 
156
 
        self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
157
 
        self.fwd_button.set_relief(gtk.RELIEF_NONE)
158
 
        self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
159
 
                                        0, 0)
160
 
        self.fwd_button.connect("clicked", self._fwd_clicked_cb)
161
 
        hbox.pack_start(self.fwd_button, expand=False, fill=True)
162
 
        self.fwd_button.show()
163
 
 
164
 
        return frame
165
 
 
166
 
    def construct_bottom(self):
167
 
        """Construct the bottom half of the window."""
168
 
        scrollwin = gtk.ScrolledWindow()
169
 
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
170
 
        scrollwin.set_shadow_type(gtk.SHADOW_NONE)
171
 
        (width, height) = self.get_size()
172
 
        scrollwin.set_size_request(width, int(height / 2.5))
173
 
        scrollwin.show()
174
 
 
175
 
        vbox = gtk.VBox(False, spacing=6)
176
 
        vbox.set_border_width(6)
177
 
        scrollwin.add_with_viewport(vbox)
178
 
        vbox.show()
179
 
 
180
 
        table = gtk.Table(rows=4, columns=2)
181
 
        table.set_row_spacings(6)
182
 
        table.set_col_spacings(6)
183
 
        vbox.pack_start(table, expand=False, fill=True)
184
 
        table.show()
185
 
 
186
 
        align = gtk.Alignment(0.0, 0.5)
187
 
        label = gtk.Label()
188
 
        label.set_markup(_('<b>Revision:</b>'))
189
 
        align.add(label)
190
 
        table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
191
 
        label.show()
192
 
        align.show()
193
 
 
194
 
        align = gtk.Alignment(0.0, 0.5)
195
 
        self.revid_label = gtk.Label()
196
 
        self.revid_label.set_selectable(True)
197
 
        align.add(self.revid_label)
198
 
        table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
199
 
        self.revid_label.show()
200
 
        align.show()
201
 
 
202
 
        align = gtk.Alignment(0.0, 0.5)
203
 
        label = gtk.Label()
204
 
        label.set_markup(_('<b>Committer:</b>'))
205
 
        align.add(label)
206
 
        table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
207
 
        label.show()
208
 
        align.show()
209
 
 
210
 
        align = gtk.Alignment(0.0, 0.5)
211
 
        self.committer_label = gtk.Label()
212
 
        self.committer_label.set_selectable(True)
213
 
        align.add(self.committer_label)
214
 
        table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
215
 
        self.committer_label.show()
216
 
        align.show()
217
 
 
218
 
        align = gtk.Alignment(0.0, 0.5)
219
 
        label = gtk.Label()
220
 
        label.set_markup(_('<b>Branch nick:</b>'))
221
 
        align.add(label)
222
 
        table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
223
 
        label.show()
224
 
        align.show()
225
 
 
226
 
        align = gtk.Alignment(0.0, 0.5)
227
 
        self.branchnick_label = gtk.Label()
228
 
        self.branchnick_label.set_selectable(True)
229
 
        align.add(self.branchnick_label)
230
 
        table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
231
 
        self.branchnick_label.show()
232
 
        align.show()
233
 
 
234
 
        align = gtk.Alignment(0.0, 0.5)
235
 
        label = gtk.Label()
236
 
        label.set_markup(_('<b>Timestamp:</b>'))
237
 
        align.add(label)
238
 
        table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
239
 
        label.show()
240
 
        align.show()
241
 
 
242
 
        align = gtk.Alignment(0.0, 0.5)
243
 
        self.timestamp_label = gtk.Label()
244
 
        self.timestamp_label.set_selectable(True)
245
 
        align.add(self.timestamp_label)
246
 
        table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
247
 
        self.timestamp_label.show()
248
 
        align.show()
249
 
 
250
 
        self.parents_table = gtk.Table(rows=1, columns=2)
251
 
        self.parents_table.set_row_spacings(3)
252
 
        self.parents_table.set_col_spacings(6)
253
 
        self.parents_table.show()
254
 
        vbox.pack_start(self.parents_table, expand=False, fill=True)
255
 
        self.parents_widgets = []
256
 
 
257
 
        label = gtk.Label()
258
 
        label.set_markup(_('<b>Parents:</b>'))
259
 
        align = gtk.Alignment(0.0, 0.5)
260
 
        align.add(label)
261
 
        self.parents_table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
262
 
        label.show()
263
 
        align.show()
264
 
 
265
 
        self.message_buffer = gtk.TextBuffer()
266
 
        textview = gtk.TextView(self.message_buffer)
267
 
        textview.set_editable(False)
268
 
        textview.set_wrap_mode(gtk.WRAP_WORD)
269
 
        textview.modify_font(pango.FontDescription("Monospace"))
270
 
        vbox.pack_start(textview, expand=True, fill=True)
271
 
        textview.show()
272
 
 
273
 
        return scrollwin
274
 
 
275
 
    def set_branch(self, branch, start, maxnum):
276
 
        """Set the branch and start position for this window.
277
 
 
278
 
        Creates a new TreeModel and populates it with information about
279
 
        the new branch before updating the window title and model of the
280
 
        treeview itself.
281
 
        """
282
 
        self.branch = branch
283
 
 
284
 
        # [ revision, node, last_lines, lines, message, committer, timestamp ]
285
 
        self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
286
 
                                   gobject.TYPE_PYOBJECT,
287
 
                                   gobject.TYPE_PYOBJECT,
288
 
                                   gobject.TYPE_PYOBJECT,
289
 
                                   str, str, str)
290
 
        self.index = {}
291
 
        index = 0
292
 
 
293
 
        last_lines = []
294
 
        (self.revisions, colours, self.children, self.parent_ids,
295
 
         merge_sorted) = distances(branch, start)
296
 
        for (index, (revision, node, lines)) in enumerate(graph(
297
 
                self.revisions, colours, merge_sorted)):
298
 
            # FIXME: at this point we should be able to show the graph order
299
 
            # and lines with no message or commit data - and then incrementally
300
 
            # fill the timestamp, committer etc data as desired.
301
 
            message = revision.message.split("\n")[0]
302
 
            if revision.committer is not None:
303
 
                timestamp = format_date(revision.timestamp, revision.timezone)
304
 
            else:
305
 
                timestamp = None
306
 
            self.model.append([revision, node, last_lines, lines,
307
 
                               message, revision.committer, timestamp])
308
 
            self.index[revision] = index
309
 
            last_lines = lines
310
 
            if maxnum is not None and index > maxnum:
311
 
                break
312
 
 
313
 
        self.set_title(branch.nick + " - bzrk")
314
 
        self.treeview.set_model(self.model)
315
 
 
316
 
    def _treeview_cursor_cb(self, *args):
317
 
        """Callback for when the treeview cursor changes."""
318
 
        (path, col) = self.treeview.get_cursor()
319
 
        revision = self.model[path][0]
320
 
 
321
 
        self.back_button.set_sensitive(len(self.parent_ids[revision]) > 0)
322
 
        self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
323
 
 
324
 
        if revision.committer is not None:
325
 
            branchnick = ""
326
 
            committer = revision.committer
327
 
            timestamp = format_date(revision.timestamp, revision.timezone)
328
 
            message = revision.message
329
 
            try:
330
 
                branchnick = revision.properties['branch-nick']
331
 
            except KeyError:
332
 
                pass
333
 
 
334
 
        else:
335
 
            committer = ""
336
 
            timestamp = ""
337
 
            message = ""
338
 
            branchnick = ""
339
 
 
340
 
        self.revid_label.set_text(revision.revision_id)
341
 
        self.branchnick_label.set_text(branchnick)
342
 
 
343
 
        self.committer_label.set_text(committer)
344
 
        self.timestamp_label.set_text(timestamp)
345
 
        self.message_buffer.set_text(message)
346
 
 
347
 
        for widget in self.parents_widgets:
348
 
            self.parents_table.remove(widget)
349
 
 
350
 
        self.parents_widgets = []
351
 
        self.parents_table.resize(max(len(self.parent_ids[revision]), 1), 2)
352
 
        
353
 
        for idx, parent_id in enumerate(self.parent_ids[revision]):
354
 
            align = gtk.Alignment(0.0, 0.0)
355
 
            self.parents_widgets.append(align)
356
 
            self.parents_table.attach(align, 1, 2, idx, idx + 1,
357
 
                                      gtk.EXPAND | gtk.FILL, gtk.FILL)
358
 
            align.show()
359
 
 
360
 
            hbox = gtk.HBox(False, spacing=6)
361
 
            align.add(hbox)
362
 
            hbox.show()
363
 
 
364
 
            image = gtk.Image()
365
 
            image.set_from_stock(
366
 
                gtk.STOCK_FIND, gtk.ICON_SIZE_SMALL_TOOLBAR)
367
 
            image.show()
368
 
 
369
 
            button = gtk.Button()
370
 
            button.add(image)
371
 
            button.set_sensitive(self.app is not None)
372
 
            button.connect("clicked", self._show_clicked_cb,
373
 
                           revision.revision_id, parent_id)
374
 
            hbox.pack_start(button, expand=False, fill=True)
375
 
            button.show()
376
 
 
377
 
            button = gtk.Button(parent_id)
378
 
            button.set_use_underline(False)
379
 
            button.connect("clicked", self._go_clicked_cb, parent_id)
380
 
            hbox.pack_start(button, expand=False, fill=True)
381
 
            button.show()
382
 
 
383
 
 
384
 
    def _back_clicked_cb(self, *args):
385
 
        """Callback for when the back button is clicked."""
386
 
        (path, col) = self.treeview.get_cursor()
387
 
        revision = self.model[path][0]
388
 
        if not len(self.parent_ids[revision]):
389
 
            return
390
 
 
391
 
        for parent_id in self.parent_ids[revision]:
392
 
            parent = self.revisions[parent_id]
393
 
            if same_branch(revision, parent):
394
 
                self.treeview.set_cursor(self.index[parent])
395
 
                break
396
 
        else:
397
 
            next = self.revisions[self.parent_ids[revision][0]]
398
 
            self.treeview.set_cursor(self.index[next])
399
 
        self.treeview.grab_focus()
400
 
 
401
 
    def _fwd_clicked_cb(self, *args):
402
 
        """Callback for when the forward button is clicked."""
403
 
        (path, col) = self.treeview.get_cursor()
404
 
        revision = self.model[path][0]
405
 
        if not len(self.children[revision]):
406
 
            return
407
 
 
408
 
        for child in self.children[revision]:
409
 
            if same_branch(child, revision):
410
 
                self.treeview.set_cursor(self.index[child])
411
 
                break
412
 
        else:
413
 
            prev = list(self.children[revision])[0]
414
 
            self.treeview.set_cursor(self.index[prev])
415
 
        self.treeview.grab_focus()
416
 
 
417
 
    def _go_clicked_cb(self, widget, revid):
418
 
        """Callback for when the go button for a parent is clicked."""
419
 
        self.treeview.set_cursor(self.index[self.revisions[revid]])
420
 
        self.treeview.grab_focus()
421
 
 
422
 
    def _show_clicked_cb(self, widget, revid, parentid):
423
 
        """Callback for when the show button for a parent is clicked."""
424
 
        if self.app is not None:
425
 
            self.app.show_diff(self.branch, revid, parentid)
426
 
        self.treeview.grab_focus()
427
 
 
428
 
    def _treeview_row_activated_cb(self, widget, path, col):
429
 
        # TODO: more than one parent
430
 
        """Callback for when a treeview row gets activated."""
431
 
        revision = self.model[path][0]
432
 
        if len(self.parent_ids[revision]) == 0:
433
 
            # Ignore revisions without parent
434
 
            return
435
 
        parent_id = self.parent_ids[revision][0]
436
 
        if self.app is not None:
437
 
            self.app.show_diff(self.branch, revision.revision_id, parent_id)
438
 
        self.treeview.grab_focus()