/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: Daniel Schierbeck
  • Date: 2007-10-14 15:54:57 UTC
  • mto: This revision was merged to the branch mainline in revision 317.
  • Revision ID: daniel.schierbeck@gmail.com-20071014155457-m3ek29p4ima8ev7d
Added the new Window base class.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
1
# -*- coding: UTF-8 -*-
3
2
"""Branch window.
4
3
 
10
9
__author__    = "Scott James Remnant <scott@ubuntu.com>"
11
10
 
12
11
 
13
 
import os
14
 
 
15
12
import gtk
16
13
import gobject
17
14
import pango
 
15
import treemodel
18
16
 
 
17
from bzrlib.plugins.gtk.window import Window
19
18
from bzrlib.osutils import format_date
20
19
 
21
 
from graph import graph
 
20
from linegraph import linegraph, same_branch
22
21
from graphcell import CellRendererGraph
23
 
 
24
 
 
25
 
class BranchWindow(gtk.Window):
 
22
from treemodel import TreeModel
 
23
 
 
24
 
 
25
class BranchWindow(Window):
26
26
    """Branch window.
27
27
 
28
28
    This object represents and manages a single window containing information
29
29
    for a particular branch.
30
30
    """
31
31
 
32
 
    def __init__(self):
33
 
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
 
32
    def __init__(self, parent=None):
 
33
        Window.__init__(self, parent=parent)
34
34
        self.set_border_width(0)
35
 
        self.set_title("bzrk")
 
35
        self.set_title("Revision history")
36
36
 
37
37
        # Use three-quarters of the screen by default
38
38
        screen = self.get_screen()
39
 
        width = int(screen.get_width() * 0.75)
40
 
        height = int(screen.get_height() * 0.75)
 
39
        monitor = screen.get_monitor_geometry(0)
 
40
        width = int(monitor.width * 0.75)
 
41
        height = int(monitor.height * 0.75)
41
42
        self.set_default_size(width, height)
42
43
 
43
44
        # FIXME AndyFitz!
44
45
        icon = self.render_icon(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)
45
46
        self.set_icon(icon)
46
47
 
 
48
        self.accel_group = gtk.AccelGroup()
 
49
        self.add_accel_group(self.accel_group)
 
50
 
47
51
        self.construct()
48
52
 
49
53
    def construct(self):
50
54
        """Construct the window contents."""
 
55
        vbox = gtk.VBox(spacing=0)
 
56
        self.add(vbox)
 
57
 
 
58
        vbox.pack_start(self.construct_navigation(), expand=False, fill=True)
 
59
        vbox.pack_start(self.construct_loading_msg(), expand=False, fill=True)
 
60
        
 
61
        paned = gtk.VPaned()
 
62
        paned.pack1(self.construct_top(), resize=True, shrink=False)
 
63
        paned.pack2(self.construct_bottom(), resize=False, shrink=True)
 
64
        paned.show()
 
65
        vbox.pack_start(paned, expand=True, fill=True)
 
66
        vbox.set_focus_child(paned)
 
67
 
 
68
        vbox.show()
 
69
    
 
70
    def construct_loading_msg(self):
 
71
        image_loading = gtk.image_new_from_stock(gtk.STOCK_REFRESH,
 
72
                                                 gtk.ICON_SIZE_BUTTON)
 
73
        image_loading.show()
 
74
        
 
75
        label_loading = gtk.Label(_("Please wait, loading ancestral graph..."))
 
76
        label_loading.set_alignment(0.0, 0.5)        
 
77
        label_loading.show()
 
78
        
 
79
        self.loading_msg_box = gtk.HBox()
 
80
        self.loading_msg_box.set_spacing(5)
 
81
        self.loading_msg_box.set_border_width(5)        
 
82
        self.loading_msg_box.pack_start(image_loading, False, False)
 
83
        self.loading_msg_box.pack_start(label_loading, True, True)
 
84
        self.loading_msg_box.show()
 
85
        
 
86
        return self.loading_msg_box
 
87
 
 
88
    def construct_top(self):
 
89
        """Construct the top-half of the window."""
51
90
        scrollwin = gtk.ScrolledWindow()
52
91
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
53
92
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
54
 
        scrollwin.set_border_width(12)
55
 
        self.add(scrollwin)
56
93
        scrollwin.show()
57
94
 
58
95
        self.treeview = gtk.TreeView()
59
96
        self.treeview.set_rules_hint(True)
60
97
        self.treeview.set_search_column(4)
 
98
        self.treeview.get_selection().connect("changed", self._treeselection_changed_cb)
 
99
        self.treeview.connect("row-activated", self._treeview_row_activated_cb)
 
100
        self.treeview.connect("button-release-event", 
 
101
                self._treeview_row_mouseclick)
 
102
        self.treeview.set_property('fixed-height-mode', True)
61
103
        scrollwin.add(self.treeview)
62
104
        self.treeview.show()
63
105
 
64
 
        cell = CellRendererGraph()
65
 
        column = gtk.TreeViewColumn()
66
 
        column.set_resizable(False)
67
 
        column.pack_start(cell, expand=False)
68
 
        column.add_attribute(cell, "node", 1)
69
 
        column.add_attribute(cell, "in-lines", 2)
70
 
        column.add_attribute(cell, "out-lines", 3)
 
106
        cell = gtk.CellRendererText()
 
107
        cell.set_property("width-chars", 15)
 
108
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
109
        column = gtk.TreeViewColumn("Revision No")
 
110
        column.set_resizable(True)
 
111
        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
112
        column.set_fixed_width(cell.get_size(self.treeview)[2])
 
113
        column.pack_start(cell, expand=True)
 
114
        column.add_attribute(cell, "text", treemodel.REVNO)
71
115
        self.treeview.append_column(column)
72
116
 
 
117
        self.graph_cell = CellRendererGraph()
 
118
        self.graph_column = gtk.TreeViewColumn()
 
119
        self.graph_column.set_resizable(True)
 
120
        self.graph_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
121
        self.graph_column.pack_start(self.graph_cell, expand=False)
 
122
        self.graph_column.add_attribute(self.graph_cell, "node", treemodel.NODE)
 
123
        self.graph_column.add_attribute(self.graph_cell, "in-lines", treemodel.LAST_LINES)
 
124
        self.graph_column.add_attribute(self.graph_cell, "out-lines", treemodel.LINES)
 
125
        self.treeview.append_column(self.graph_column)
 
126
 
73
127
        cell = gtk.CellRendererText()
74
 
        cell.set_property("width-chars", 40)
 
128
        cell.set_property("width-chars", 65)
75
129
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
76
130
        column = gtk.TreeViewColumn("Message")
77
131
        column.set_resizable(True)
 
132
        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
133
        column.set_fixed_width(cell.get_size(self.treeview)[2])
78
134
        column.pack_start(cell, expand=True)
79
 
        column.add_attribute(cell, "text", 4)
 
135
        column.add_attribute(cell, "text", treemodel.MESSAGE)
80
136
        self.treeview.append_column(column)
81
137
 
82
138
        cell = gtk.CellRendererText()
83
 
        cell.set_property("width-chars", 40)
 
139
        cell.set_property("width-chars", 15)
84
140
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
85
141
        column = gtk.TreeViewColumn("Committer")
86
142
        column.set_resizable(True)
87
 
        column.pack_start(cell, expand=True)
88
 
        column.add_attribute(cell, "text", 5)
89
 
        self.treeview.append_column(column)
90
 
 
91
 
        cell = gtk.CellRendererText()
92
 
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
93
 
        column = gtk.TreeViewColumn("Date")
94
 
        column.set_resizable(True)
95
 
        column.pack_start(cell, expand=True)
96
 
        column.add_attribute(cell, "text", 6)
97
 
        self.treeview.append_column(column)
98
 
 
99
 
    def set_branch(self, branch, start):
 
143
        column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
144
        column.set_fixed_width(cell.get_size(self.treeview)[2])
 
145
        column.pack_start(cell, expand=True)
 
146
        column.add_attribute(cell, "text", treemodel.COMMITER)
 
147
        self.treeview.append_column(column)
 
148
 
 
149
        return scrollwin
 
150
 
 
151
    def construct_navigation(self):
 
152
        """Construct the navigation buttons."""
 
153
        frame = gtk.Frame()
 
154
        frame.set_shadow_type(gtk.SHADOW_OUT)
 
155
        frame.show()
 
156
        
 
157
        hbox = gtk.HBox(spacing=12)
 
158
        frame.add(hbox)
 
159
        hbox.show()
 
160
 
 
161
        self.back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
 
162
        self.back_button.set_relief(gtk.RELIEF_NONE)
 
163
        self.back_button.add_accelerator("clicked", self.accel_group, ord('['),
 
164
                                         0, 0)
 
165
        self.back_button.connect("clicked", self._back_clicked_cb)
 
166
        hbox.pack_start(self.back_button, expand=False, fill=True)
 
167
        self.back_button.show()
 
168
 
 
169
        self.fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
 
170
        self.fwd_button.set_relief(gtk.RELIEF_NONE)
 
171
        self.fwd_button.add_accelerator("clicked", self.accel_group, ord(']'),
 
172
                                        0, 0)
 
173
        self.fwd_button.connect("clicked", self._fwd_clicked_cb)
 
174
        hbox.pack_start(self.fwd_button, expand=False, fill=True)
 
175
        self.fwd_button.show()
 
176
 
 
177
        return frame
 
178
 
 
179
    def construct_bottom(self):
 
180
        """Construct the bottom half of the window."""
 
181
        from bzrlib.plugins.gtk.logview import LogView
 
182
        self.logview = LogView(None, True, [], True)
 
183
        (width, height) = self.get_size()
 
184
        self.logview.set_size_request(width, int(height / 2.5))
 
185
        self.logview.show()
 
186
        self.logview.set_show_callback(self._show_clicked_cb)
 
187
        self.logview.set_go_callback(self._go_clicked_cb)
 
188
        return self.logview
 
189
 
 
190
    def set_branch(self, branch, start, maxnum):
100
191
        """Set the branch and start position for this window.
101
192
 
102
193
        Creates a new TreeModel and populates it with information about
103
194
        the new branch before updating the window title and model of the
104
195
        treeview itself.
105
196
        """
106
 
        # [ revision, node, last_lines, lines, message, committer, timestamp ]
107
 
        model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
108
 
                              gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
109
 
                              str, str, str)
110
 
 
111
 
        last_lines = []
112
 
        for revision, node, lines in graph(branch, start):
113
 
            message = revision.message.split("\n")[0]
114
 
            if revision.committer is not None:
115
 
                timestamp = format_date(revision.timestamp, revision.timezone)
116
 
            else:
117
 
                timestamp = None
118
 
 
119
 
            model.append([ revision, node, last_lines, lines,
120
 
                           message, revision.committer, timestamp ])
121
 
            last_lines = lines
122
 
 
123
 
        self.set_title(os.path.basename(branch.base) + " - bzrk")
124
 
        self.treeview.set_model(model)
 
197
        self.branch = branch
 
198
        self.set_title(branch.nick + " - revision history")
 
199
        gobject.idle_add(self.populate_model, start, maxnum)
 
200
 
 
201
    def populate_model(self, start, maxnum):
 
202
        (linegraphdata, index, columns_len) = linegraph(self.branch,
 
203
                                                        start,
 
204
                                                        maxnum)
 
205
        self.model = TreeModel(self.branch, linegraphdata)
 
206
        self.graph_cell.columns_len = columns_len
 
207
        width = self.graph_cell.get_size(self.treeview)[2]
 
208
        self.graph_column.set_fixed_width(width)
 
209
        self.graph_column.set_max_width(width)
 
210
        self.index = index
 
211
        self.treeview.set_model(self.model)
 
212
        self.treeview.get_selection().select_path(0)
 
213
        self.loading_msg_box.hide()
 
214
        return False
 
215
    
 
216
    def _treeselection_changed_cb(self, selection, *args):
 
217
        """Callback for when the treeview changes."""
 
218
        (model, selected_rows) = selection.get_selected_rows()
 
219
        if len(selected_rows) > 0:
 
220
            iter = self.model.get_iter(selected_rows[0])
 
221
            revision = self.model.get_value(iter, treemodel.REVISION)
 
222
            parents = self.model.get_value(iter, treemodel.PARENTS)
 
223
            children = self.model.get_value(iter, treemodel.CHILDREN)
 
224
            
 
225
            self.back_button.set_sensitive(len(parents) > 0)
 
226
            self.fwd_button.set_sensitive(len(children) > 0)
 
227
            tags = []
 
228
            if self.branch.supports_tags():
 
229
                tagdict = self.branch.tags.get_reverse_tag_dict()
 
230
                if tagdict.has_key(revision.revision_id):
 
231
                    tags = tagdict[revision.revision_id]
 
232
            self.logview.set_revision(revision, tags, children)
 
233
 
 
234
    def _back_clicked_cb(self, *args):
 
235
        """Callback for when the back button is clicked."""
 
236
        (path, col) = self.treeview.get_cursor()
 
237
        revision = self.model[path][0]
 
238
        parents = self.model[path][4]
 
239
        if not len(parents):
 
240
            return
 
241
 
 
242
        for parent_id in parents:
 
243
            parent = self.revisions[self.index[parent_id]]
 
244
            if same_branch(revision, parent):
 
245
                self.treeview.set_cursor(self.index[parent_id])
 
246
                break
 
247
        else:
 
248
            self.treeview.set_cursor(self.index[parents[0]])
 
249
        self.treeview.grab_focus()
 
250
 
 
251
    def _fwd_clicked_cb(self, *args):
 
252
        """Callback for when the forward button is clicked."""
 
253
        (path, col) = self.treeview.get_cursor()
 
254
        revision = self.model[path][0]
 
255
        children = self.model[path][5]
 
256
        if not len(children):
 
257
            return
 
258
 
 
259
        for child_id in children:
 
260
            child = self.revisions[self.index[child_id]]
 
261
            if same_branch(child, revision):
 
262
                self.treeview.set_cursor(self.index[child_id])
 
263
                break
 
264
        else:
 
265
            self.treeview.set_cursor(self.index[children[0]])
 
266
        self.treeview.grab_focus()
 
267
 
 
268
    def _go_clicked_cb(self, revid):
 
269
        """Callback for when the go button for a parent is clicked."""
 
270
        self.treeview.set_cursor(self.index[revid])
 
271
        self.treeview.grab_focus()
 
272
 
 
273
    def show_diff(self, branch, revid, parentid):
 
274
        """Open a new window to show a diff between the given revisions."""
 
275
        from bzrlib.plugins.gtk.diff import DiffWindow
 
276
        window = DiffWindow(parent=self)
 
277
        (parent_tree, rev_tree) = branch.repository.revision_trees([parentid, 
 
278
                                                                   revid])
 
279
        description = revid + " - " + branch.nick
 
280
        window.set_diff(description, rev_tree, parent_tree)
 
281
        window.show()
 
282
 
 
283
    def _show_clicked_cb(self, revid, parentid):
 
284
        """Callback for when the show button for a parent is clicked."""
 
285
        self.show_diff(self.branch, revid, parentid)
 
286
        self.treeview.grab_focus()
 
287
 
 
288
    def _treeview_row_mouseclick(self, widget, event):
 
289
        from bzrlib.plugins.gtk.revisionmenu import RevisionPopupMenu
 
290
        if event.button == 3:
 
291
            menu = RevisionPopupMenu(self.branch.repository, 
 
292
                [x.revision_id for x in self.selected_revisions()],
 
293
                self.branch)
 
294
            menu.popup(None, None, None, event.button, event.get_time())
 
295
 
 
296
    def selected_revision(self, path):
 
297
        return self.model[path][treemodel.REVISION]
 
298
 
 
299
    def selected_revisions(self):
 
300
        return [self.selected_revision(path) for path in \
 
301
                self.treeview.get_selection().get_selected_rows()[1]]
 
302
 
 
303
    def _treeview_row_activated_cb(self, widget, path, col):
 
304
        # TODO: more than one parent
 
305
        """Callback for when a treeview row gets activated."""
 
306
        revision_id = self.model[path][treemodel.REVID]
 
307
        parents = self.model[path][treemodel.PARENTS]
 
308
        if len(parents) == 0:
 
309
            # Ignore revisions without parent
 
310
            return
 
311
        parent_id = parents[0]
 
312
        self.show_diff(self.branch, revision_id, parent_id)
 
313
        self.treeview.grab_focus()