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

  • Committer: Jelmer Vernooij
  • Date: 2007-02-01 15:50:40 UTC
  • Revision ID: jelmer@samba.org-20070201155040-3hq4mfbxs99kzazy
add framework for tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
import gtk
13
13
import gobject
14
14
import pango
15
 
import treemodel
16
15
 
17
16
from bzrlib.osutils import format_date
18
17
 
19
 
from linegraph import linegraph, same_branch
 
18
from graph import distances, graph, same_branch
20
19
from graphcell import CellRendererGraph
21
 
from treemodel import TreeModel
22
20
 
23
21
 
24
22
class BranchWindow(gtk.Window):
28
26
    for a particular branch.
29
27
    """
30
28
 
31
 
    def __init__(self):
 
29
    def __init__(self, app=None):
32
30
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
33
31
        self.set_border_width(0)
34
32
        self.set_title("bzrk")
35
33
 
 
34
        self.app = app
 
35
 
36
36
        # Use three-quarters of the screen by default
37
37
        screen = self.get_screen()
38
38
        monitor = screen.get_monitor_geometry(0)
77
77
        self.treeview.set_search_column(4)
78
78
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
79
79
        self.treeview.connect("row-activated", self._treeview_row_activated_cb)
80
 
        self.treeview.connect("button-release-event", 
81
 
                self._treeview_row_mouseclick)
82
80
        scrollwin.add(self.treeview)
83
81
        self.treeview.show()
84
82
 
85
 
        cell = gtk.CellRendererText()
86
 
        #cell.set_property("width-chars", 40)
87
 
        #cell.set_property("ellipsize", pango.ELLIPSIZE_END)
88
 
        column = gtk.TreeViewColumn("Revision No")
89
 
        column.set_resizable(True)
90
 
        column.pack_start(cell, expand=True)
91
 
        column.add_attribute(cell, "text", treemodel.REVNO)
92
 
        self.treeview.append_column(column)
93
 
 
94
83
        cell = CellRendererGraph()
95
84
        column = gtk.TreeViewColumn()
96
85
        column.set_resizable(True)
97
86
        column.pack_start(cell, expand=False)
98
 
        column.add_attribute(cell, "node", treemodel.NODE)
99
 
        column.add_attribute(cell, "in-lines", treemodel.LAST_LINES)
100
 
        column.add_attribute(cell, "out-lines", treemodel.LINES)
 
87
        column.add_attribute(cell, "node", 1)
 
88
        column.add_attribute(cell, "in-lines", 2)
 
89
        column.add_attribute(cell, "out-lines", 3)
101
90
        self.treeview.append_column(column)
102
91
 
103
92
        cell = gtk.CellRendererText()
106
95
        column = gtk.TreeViewColumn("Message")
107
96
        column.set_resizable(True)
108
97
        column.pack_start(cell, expand=True)
109
 
        column.add_attribute(cell, "text", treemodel.MESSAGE)
 
98
        column.add_attribute(cell, "text", 4)
110
99
        self.treeview.append_column(column)
111
100
 
112
101
        cell = gtk.CellRendererText()
115
104
        column = gtk.TreeViewColumn("Committer")
116
105
        column.set_resizable(True)
117
106
        column.pack_start(cell, expand=True)
118
 
        column.add_attribute(cell, "text", treemodel.COMMITER)
 
107
        column.add_attribute(cell, "text", 5)
119
108
        self.treeview.append_column(column)
120
109
 
121
110
        cell = gtk.CellRendererText()
123
112
        column = gtk.TreeViewColumn("Date")
124
113
        column.set_resizable(True)
125
114
        column.pack_start(cell, expand=True)
126
 
        column.add_attribute(cell, "text", treemodel.TIMESTAMP)
 
115
        column.add_attribute(cell, "text", 6)
127
116
        self.treeview.append_column(column)
128
117
 
129
118
        return scrollwin
158
147
 
159
148
    def construct_bottom(self):
160
149
        """Construct the bottom half of the window."""
161
 
        from bzrlib.plugins.gtk.logview import LogView
162
 
        self.logview = LogView(None, True, [], True)
 
150
        scrollwin = gtk.ScrolledWindow()
 
151
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
152
        scrollwin.set_shadow_type(gtk.SHADOW_NONE)
163
153
        (width, height) = self.get_size()
164
 
        self.logview.set_size_request(width, int(height / 2.5))
165
 
        self.logview.show()
166
 
        self.logview.set_show_callback(self._show_clicked_cb)
167
 
        self.logview.set_go_callback(self._go_clicked_cb)
168
 
        return self.logview
 
154
        scrollwin.set_size_request(width, int(height / 2.5))
 
155
        scrollwin.show()
 
156
 
 
157
        vbox = gtk.VBox(False, spacing=6)
 
158
        vbox.set_border_width(6)
 
159
        scrollwin.add_with_viewport(vbox)
 
160
        vbox.show()
 
161
 
 
162
        table = gtk.Table(rows=4, columns=2)
 
163
        table.set_row_spacings(6)
 
164
        table.set_col_spacings(6)
 
165
        vbox.pack_start(table, expand=False, fill=True)
 
166
        table.show()
 
167
 
 
168
        align = gtk.Alignment(0.0, 0.5)
 
169
        label = gtk.Label()
 
170
        label.set_markup("<b>Revision:</b>")
 
171
        align.add(label)
 
172
        table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
173
        label.show()
 
174
        align.show()
 
175
 
 
176
        align = gtk.Alignment(0.0, 0.5)
 
177
        self.revid_label = gtk.Label()
 
178
        self.revid_label.set_selectable(True)
 
179
        align.add(self.revid_label)
 
180
        table.attach(align, 1, 2, 0, 1, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
181
        self.revid_label.show()
 
182
        align.show()
 
183
 
 
184
        align = gtk.Alignment(0.0, 0.5)
 
185
        label = gtk.Label()
 
186
        label.set_markup("<b>Committer:</b>")
 
187
        align.add(label)
 
188
        table.attach(align, 0, 1, 1, 2, gtk.FILL, gtk.FILL)
 
189
        label.show()
 
190
        align.show()
 
191
 
 
192
        align = gtk.Alignment(0.0, 0.5)
 
193
        self.committer_label = gtk.Label()
 
194
        self.committer_label.set_selectable(True)
 
195
        align.add(self.committer_label)
 
196
        table.attach(align, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
197
        self.committer_label.show()
 
198
        align.show()
 
199
 
 
200
        align = gtk.Alignment(0.0, 0.5)
 
201
        label = gtk.Label()
 
202
        label.set_markup("<b>Branch nick:</b>")
 
203
        align.add(label)
 
204
        table.attach(align, 0, 1, 2, 3, gtk.FILL, gtk.FILL)
 
205
        label.show()
 
206
        align.show()
 
207
 
 
208
        align = gtk.Alignment(0.0, 0.5)
 
209
        self.branchnick_label = gtk.Label()
 
210
        self.branchnick_label.set_selectable(True)
 
211
        align.add(self.branchnick_label)
 
212
        table.attach(align, 1, 2, 2, 3, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
213
        self.branchnick_label.show()
 
214
        align.show()
 
215
 
 
216
        align = gtk.Alignment(0.0, 0.5)
 
217
        label = gtk.Label()
 
218
        label.set_markup("<b>Timestamp:</b>")
 
219
        align.add(label)
 
220
        table.attach(align, 0, 1, 3, 4, gtk.FILL, gtk.FILL)
 
221
        label.show()
 
222
        align.show()
 
223
 
 
224
        align = gtk.Alignment(0.0, 0.5)
 
225
        self.timestamp_label = gtk.Label()
 
226
        self.timestamp_label.set_selectable(True)
 
227
        align.add(self.timestamp_label)
 
228
        table.attach(align, 1, 2, 3, 4, gtk.EXPAND | gtk.FILL, gtk.FILL)
 
229
        self.timestamp_label.show()
 
230
        align.show()
 
231
 
 
232
        self.parents_table = gtk.Table(rows=1, columns=2)
 
233
        self.parents_table.set_row_spacings(3)
 
234
        self.parents_table.set_col_spacings(6)
 
235
        self.parents_table.show()
 
236
        vbox.pack_start(self.parents_table, expand=False, fill=True)
 
237
        self.parents_widgets = []
 
238
 
 
239
        label = gtk.Label()
 
240
        label.set_markup("<b>Parents:</b>")
 
241
        align = gtk.Alignment(0.0, 0.5)
 
242
        align.add(label)
 
243
        self.parents_table.attach(align, 0, 1, 0, 1, gtk.FILL, gtk.FILL)
 
244
        label.show()
 
245
        align.show()
 
246
 
 
247
        self.message_buffer = gtk.TextBuffer()
 
248
        textview = gtk.TextView(self.message_buffer)
 
249
        textview.set_editable(False)
 
250
        textview.set_wrap_mode(gtk.WRAP_WORD)
 
251
        textview.modify_font(pango.FontDescription("Monospace"))
 
252
        vbox.pack_start(textview, expand=True, fill=True)
 
253
        textview.show()
 
254
 
 
255
        return scrollwin
169
256
 
170
257
    def set_branch(self, branch, start, maxnum):
171
258
        """Set the branch and start position for this window.
172
 
        
 
259
 
173
260
        Creates a new TreeModel and populates it with information about
174
261
        the new branch before updating the window title and model of the
175
262
        treeview itself.
176
263
        """
177
264
        self.branch = branch
 
265
 
 
266
        # [ revision, node, last_lines, lines, message, committer, timestamp ]
 
267
        self.model = gtk.ListStore(gobject.TYPE_PYOBJECT,
 
268
                                   gobject.TYPE_PYOBJECT,
 
269
                                   gobject.TYPE_PYOBJECT,
 
270
                                   gobject.TYPE_PYOBJECT,
 
271
                                   str, str, str)
 
272
        self.index = {}
 
273
        index = 0
 
274
 
 
275
        last_lines = []
 
276
        (self.revisions, colours, self.children, self.parent_ids,
 
277
         merge_sorted) = distances(branch, start)
 
278
        for (index, (revision, node, lines)) in enumerate(graph(
 
279
                self.revisions, colours, merge_sorted)):
 
280
            # FIXME: at this point we should be able to show the graph order
 
281
            # and lines with no message or commit data - and then incrementally
 
282
            # fill the timestamp, committer etc data as desired.
 
283
            message = revision.message.split("\n")[0]
 
284
            if revision.committer is not None:
 
285
                timestamp = format_date(revision.timestamp, revision.timezone)
 
286
            else:
 
287
                timestamp = None
 
288
            self.model.append([revision, node, last_lines, lines,
 
289
                               message, revision.committer, timestamp])
 
290
            self.index[revision] = index
 
291
            last_lines = lines
 
292
            if maxnum is not None and index > maxnum:
 
293
                break
 
294
 
178
295
        self.set_title(branch.nick + " - bzrk")
179
 
        gobject.idle_add(self.populate_model, start, maxnum)
180
 
 
181
 
    def populate_model(self, start, maxnum):
182
 
        (linegraphdata, index) = linegraph(self.branch,
183
 
                                           start,
184
 
                                           maxnum)
185
 
        self.model = TreeModel(self.branch, linegraphdata)
186
 
        self.index = index
187
296
        self.treeview.set_model(self.model)
188
297
 
189
298
    def _treeview_cursor_cb(self, *args):
190
299
        """Callback for when the treeview cursor changes."""
191
300
        (path, col) = self.treeview.get_cursor()
192
 
        iter = self.model.get_iter(path)
193
 
        revision = self.model.get_value(iter, treemodel.REVISION)
194
 
        parents = self.model.get_value(iter, treemodel.PARENTS)
195
 
        children = self.model.get_value(iter, treemodel.CHILDREN)
196
 
 
197
 
        self.back_button.set_sensitive(len(parents) > 0)
198
 
        self.fwd_button.set_sensitive(len(children) > 0)
199
 
        tags = []
200
 
        if self.branch.supports_tags():
201
 
            tagdict = self.branch.tags.get_reverse_tag_dict()
202
 
            if tagdict.has_key(revision.revision_id):
203
 
                tags = tagdict[revision.revision_id]
204
 
        self.logview.set_revision(revision, tags, children)
 
301
        revision = self.model[path][0]
 
302
 
 
303
        self.back_button.set_sensitive(len(self.parent_ids[revision]) > 0)
 
304
        self.fwd_button.set_sensitive(len(self.children[revision]) > 0)
 
305
 
 
306
        if revision.committer is not None:
 
307
            branchnick = ""
 
308
            committer = revision.committer
 
309
            timestamp = format_date(revision.timestamp, revision.timezone)
 
310
            message = revision.message
 
311
            try:
 
312
                branchnick = revision.properties['branch-nick']
 
313
            except KeyError:
 
314
                pass
 
315
 
 
316
        else:
 
317
            committer = ""
 
318
            timestamp = ""
 
319
            message = ""
 
320
            branchnick = ""
 
321
 
 
322
        self.revid_label.set_text(revision.revision_id)
 
323
        self.branchnick_label.set_text(branchnick)
 
324
 
 
325
        self.committer_label.set_text(committer)
 
326
        self.timestamp_label.set_text(timestamp)
 
327
        self.message_buffer.set_text(message)
 
328
 
 
329
        for widget in self.parents_widgets:
 
330
            self.parents_table.remove(widget)
 
331
 
 
332
        self.parents_widgets = []
 
333
        self.parents_table.resize(max(len(self.parent_ids[revision]), 1), 2)
 
334
        
 
335
        for idx, parent_id in enumerate(self.parent_ids[revision]):
 
336
            align = gtk.Alignment(0.0, 0.0)
 
337
            self.parents_widgets.append(align)
 
338
            self.parents_table.attach(align, 1, 2, idx, idx + 1,
 
339
                                      gtk.EXPAND | gtk.FILL, gtk.FILL)
 
340
            align.show()
 
341
 
 
342
            hbox = gtk.HBox(False, spacing=6)
 
343
            align.add(hbox)
 
344
            hbox.show()
 
345
 
 
346
            image = gtk.Image()
 
347
            image.set_from_stock(
 
348
                gtk.STOCK_FIND, gtk.ICON_SIZE_SMALL_TOOLBAR)
 
349
            image.show()
 
350
 
 
351
            button = gtk.Button()
 
352
            button.add(image)
 
353
            button.set_sensitive(self.app is not None)
 
354
            button.connect("clicked", self._show_clicked_cb,
 
355
                           revision.revision_id, parent_id)
 
356
            hbox.pack_start(button, expand=False, fill=True)
 
357
            button.show()
 
358
 
 
359
            button = gtk.Button(parent_id)
 
360
            button.set_use_underline(False)
 
361
            button.connect("clicked", self._go_clicked_cb, parent_id)
 
362
            hbox.pack_start(button, expand=False, fill=True)
 
363
            button.show()
 
364
 
205
365
 
206
366
    def _back_clicked_cb(self, *args):
207
367
        """Callback for when the back button is clicked."""
208
368
        (path, col) = self.treeview.get_cursor()
209
369
        revision = self.model[path][0]
210
 
        parents = self.model[path][4]
211
 
        if not len(parents):
 
370
        if not len(self.parent_ids[revision]):
212
371
            return
213
372
 
214
 
        for parent_id in parents:
215
 
            parent = self.revisions[self.index[parent_id]]
 
373
        for parent_id in self.parent_ids[revision]:
 
374
            parent = self.revisions[parent_id]
216
375
            if same_branch(revision, parent):
217
 
                self.treeview.set_cursor(self.index[parent_id])
 
376
                self.treeview.set_cursor(self.index[parent])
218
377
                break
219
378
        else:
220
 
            self.treeview.set_cursor(self.index[parents[0]])
 
379
            next = self.revisions[self.parent_ids[revision][0]]
 
380
            self.treeview.set_cursor(self.index[next])
221
381
        self.treeview.grab_focus()
222
382
 
223
383
    def _fwd_clicked_cb(self, *args):
224
384
        """Callback for when the forward button is clicked."""
225
385
        (path, col) = self.treeview.get_cursor()
226
386
        revision = self.model[path][0]
227
 
        children = self.model[path][5]
228
 
        if not len(children):
 
387
        if not len(self.children[revision]):
229
388
            return
230
389
 
231
 
        for child_id in children:
232
 
            child = self.revisions[self.index[child_id]]
 
390
        for child in self.children[revision]:
233
391
            if same_branch(child, revision):
234
 
                self.treeview.set_cursor(self.index[child_id])
 
392
                self.treeview.set_cursor(self.index[child])
235
393
                break
236
394
        else:
237
 
            self.treeview.set_cursor(self.index[children[0]])
 
395
            prev = list(self.children[revision])[0]
 
396
            self.treeview.set_cursor(self.index[prev])
238
397
        self.treeview.grab_focus()
239
398
 
240
 
    def _go_clicked_cb(self, revid):
 
399
    def _go_clicked_cb(self, widget, revid):
241
400
        """Callback for when the go button for a parent is clicked."""
242
 
        self.treeview.set_cursor(self.index[revid])
 
401
        self.treeview.set_cursor(self.index[self.revisions[revid]])
243
402
        self.treeview.grab_focus()
244
403
 
245
 
    def show_diff(self, branch, revid, parentid):
246
 
        """Open a new window to show a diff between the given revisions."""
247
 
        from bzrlib.plugins.gtk.diff import DiffWindow
248
 
        window = DiffWindow()
249
 
        (parent_tree, rev_tree) = branch.repository.revision_trees([parentid, 
250
 
                                                                   revid])
251
 
        description = revid + " - " + branch.nick
252
 
        window.set_diff(description, rev_tree, parent_tree)
253
 
        window.show()
254
 
 
255
 
    def _show_clicked_cb(self, revid, parentid):
 
404
    def _show_clicked_cb(self, widget, revid, parentid):
256
405
        """Callback for when the show button for a parent is clicked."""
257
 
        self.show_diff(self.branch, revid, parentid)
 
406
        if self.app is not None:
 
407
            self.app.show_diff(self.branch, revid, parentid)
258
408
        self.treeview.grab_focus()
259
409
 
260
 
    def _treeview_row_mouseclick(self, widget, event):
261
 
        from bzrlib.plugins.gtk.revisionmenu import RevisionPopupMenu
262
 
        if event.button == 3:
263
 
            menu = RevisionPopupMenu(self.branch.repository, 
264
 
                [x.revision_id for x in self.selected_revisions()],
265
 
                self.branch)
266
 
            menu.popup(None, None, None, event.button, event.get_time())
267
 
 
268
 
    def selected_revision(self, path):
269
 
        return self.model[path][0]
270
 
 
271
 
    def selected_revisions(self):
272
 
        return [self.selected_revision(path) for path in \
273
 
                self.treeview.get_selection().get_selected_rows()[1]]
274
 
 
275
410
    def _treeview_row_activated_cb(self, widget, path, col):
276
411
        # TODO: more than one parent
277
412
        """Callback for when a treeview row gets activated."""
278
 
        revision = self.selected_revision(path)
 
413
        revision = self.model[path][0]
279
414
        if len(self.parent_ids[revision]) == 0:
280
415
            # Ignore revisions without parent
281
416
            return
282
417
        parent_id = self.parent_ids[revision][0]
283
 
        self.show_diff(self.branch, revision.revision_id, parent_id)
 
418
        if self.app is not None:
 
419
            self.app.show_diff(self.branch, revision.revision_id, parent_id)
284
420
        self.treeview.grab_focus()