/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/gannotate.py

  • Committer: Jelmer Vernooij
  • Date: 2006-09-27 17:40:05 UTC
  • mto: (0.12.2 olive)
  • mto: This revision was merged to the branch mainline in revision 83.
  • Revision ID: jelmer@samba.org-20060927174005-5dfa27de4081ed19
Handle non-bzr unknown errors as well.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Dan Loda <danloda@gmail.com>
2
 
 
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
 
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
 
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
import time
18
 
 
19
 
import pygtk
20
 
pygtk.require("2.0")
21
 
import gobject
22
 
import gtk
23
 
import pango
24
 
 
25
 
from bzrlib.errors import NoSuchRevision
26
 
 
27
 
from colormap import AnnotateColorMap, AnnotateColorSaturation
28
 
from logview import LogView
29
 
from spanselector import SpanSelector
30
 
 
31
 
 
32
 
(
33
 
    REVISION_ID_COL,
34
 
    LINE_NUM_COL,
35
 
    COMMITTER_COL,
36
 
    REVNO_COL,
37
 
    HIGHLIGHT_COLOR_COL,
38
 
    TEXT_LINE_COL
39
 
) = range(6)
40
 
 
41
 
 
42
 
class GAnnotateWindow(gtk.Window):
43
 
    """Annotate window."""
44
 
 
45
 
    def __init__(self, all=False, plain=False):
46
 
        self.all = all
47
 
        self.plain = plain
48
 
        
49
 
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
50
 
        
51
 
        self.set_icon(self.render_icon(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON))
52
 
        self.annotate_colormap = AnnotateColorSaturation()
53
 
 
54
 
        self._create()
55
 
 
56
 
        if self.plain:
57
 
            self.span_selector.hide()
58
 
 
59
 
    def annotate(self, branch, file_id):
60
 
        self.revisions = {}
61
 
        
62
 
        # [revision id, line number, committer, revno, highlight color, line]
63
 
        self.annomodel = gtk.ListStore(gobject.TYPE_STRING,
64
 
                                       gobject.TYPE_STRING,
65
 
                                       gobject.TYPE_STRING,
66
 
                                       gobject.TYPE_STRING,
67
 
                                       gobject.TYPE_STRING,
68
 
                                       gobject.TYPE_STRING)
69
 
        
70
 
        last_seen = None
71
 
        try:
72
 
            branch.lock_read()
73
 
            branch.repository.lock_read()
74
 
            for line_no, (revision, revno, line)\
75
 
                    in enumerate(self._annotate(branch, file_id)):
76
 
                if revision.revision_id == last_seen and not self.all:
77
 
                    revno = committer = ""
78
 
                else:
79
 
                    last_seen = revision.revision_id
80
 
                    committer = revision.committer
81
 
 
82
 
                if revision.revision_id not in self.revisions:
83
 
                    self.revisions[revision.revision_id] = revision
84
 
 
85
 
                self.annomodel.append([revision.revision_id,
86
 
                                       line_no + 1,
87
 
                                       committer,
88
 
                                       revno,
89
 
                                       None,
90
 
                                       line.rstrip("\r\n")
91
 
                                      ])
92
 
 
93
 
            if not self.plain:
94
 
                self._set_oldest_newest()
95
 
                # Recall that calling activate_default will emit "span-changed",
96
 
                # so self._span_changed_cb will take care of initial highlighting
97
 
                self.span_selector.activate_default()
98
 
        finally:
99
 
            branch.repository.unlock()
100
 
            branch.unlock()
101
 
 
102
 
        self.annoview.set_model(self.annomodel)
103
 
        self.annoview.grab_focus()
104
 
 
105
 
    def jump_to_line(self, lineno):
106
 
        if lineno > len(self.annomodel) or lineno < 1:
107
 
            row = 0
108
 
            # FIXME:should really deal with this in the gui. Perhaps a status
109
 
            # bar?
110
 
            print("gannotate: Line number %d does't exist. Defaulting to "
111
 
                  "line 1." % lineno)
112
 
        else:
113
 
            row = lineno - 1
114
 
 
115
 
        self.annoview.set_cursor(row)
116
 
 
117
 
    def _annotate(self, branch, file_id):
118
 
        rev_hist = branch.revision_history()
119
 
        repository = branch.repository
120
 
        rev_tree = repository.revision_tree(branch.last_revision())
121
 
        rev_id = rev_tree.inventory[file_id].revision
122
 
        weave = repository.weave_store.get_weave(file_id,
123
 
                                                 branch.get_transaction())
124
 
        
125
 
        revision_cache = RevisionCache(repository)
126
 
        for origin, text in weave.annotate_iter(rev_id):
127
 
            rev_id = origin
128
 
            try:
129
 
                revision = revision_cache.get_revision(rev_id)
130
 
                if rev_id in rev_hist:
131
 
                    revno = branch.revision_id_to_revno(rev_id)
132
 
                else:
133
 
                    revno = "merge"
134
 
            except NoSuchRevision:
135
 
                revision = NoneRevision(rev_id)
136
 
                revno = "?"
137
 
 
138
 
            yield revision, revno, text
139
 
 
140
 
    def _set_oldest_newest(self):
141
 
        rev_dates = map(lambda i: self.revisions[i].timestamp, self.revisions)
142
 
        oldest = min(rev_dates)
143
 
        newest = max(rev_dates)
144
 
 
145
 
        span = self._span_from_seconds(time.time() - oldest)
146
 
        self.span_selector.set_to_oldest_span(span)
147
 
        
148
 
        span = self._span_from_seconds(newest - oldest)
149
 
        self.span_selector.set_newest_to_oldest_span(span)
150
 
 
151
 
    def _span_from_seconds(self, seconds):
152
 
        return (seconds / (24 * 60 * 60))
153
 
    
154
 
    def _span_changed_cb(self, w, span):
155
 
        self.annotate_colormap.set_span(span)
156
 
        now = time.time()
157
 
        self.annomodel.foreach(self._highlight_annotation, now)
158
 
 
159
 
    def _highlight_annotation(self, model, path, iter, now):
160
 
        revision_id, = model.get(iter, REVISION_ID_COL)
161
 
        revision = self.revisions[revision_id]
162
 
        model.set(iter, HIGHLIGHT_COLOR_COL,
163
 
                  self.annotate_colormap.get_color(revision, now))
164
 
 
165
 
    def _show_log(self, w):
166
 
        (path, col) = self.annoview.get_cursor()
167
 
        rev_id = self.annomodel[path][REVISION_ID_COL]
168
 
        self.logview.set_revision(self.revisions[rev_id])
169
 
 
170
 
    def _create(self):
171
 
        self.logview = self._create_log_view()
172
 
        self.annoview = self._create_annotate_view()
173
 
        self.span_selector = self._create_span_selector()
174
 
 
175
 
        vbox = gtk.VBox(False, 12)
176
 
        vbox.set_border_width(12)
177
 
        vbox.show()
178
 
 
179
 
        sw = gtk.ScrolledWindow()
180
 
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
181
 
        sw.set_shadow_type(gtk.SHADOW_IN)
182
 
        sw.add(self.annoview)
183
 
        sw.show()
184
 
        
185
 
        self.pane = pane = gtk.VPaned()
186
 
        pane.add1(sw)
187
 
        pane.add2(self.logview)
188
 
        pane.show()
189
 
        vbox.pack_start(pane, expand=True, fill=True)
190
 
        
191
 
        hbox = gtk.HBox(True, 6)
192
 
        hbox.pack_start(self.span_selector, expand=False, fill=True)
193
 
        hbox.pack_start(self._create_button_box(), expand=False, fill=True)
194
 
        hbox.show()
195
 
        vbox.pack_start(hbox, expand=False, fill=True)
196
 
 
197
 
        self.add(vbox)
198
 
 
199
 
    def _create_annotate_view(self):
200
 
        tv = gtk.TreeView()
201
 
        tv.set_rules_hint(False)
202
 
        tv.connect("cursor-changed", self._show_log)
203
 
        tv.show()
204
 
 
205
 
        cell = gtk.CellRendererText()
206
 
        cell.set_property("xalign", 1.0)
207
 
        cell.set_property("ypad", 0)
208
 
        cell.set_property("family", "Monospace")
209
 
        cell.set_property("cell-background-gdk",
210
 
                          tv.get_style().bg[gtk.STATE_NORMAL])
211
 
        col = gtk.TreeViewColumn()
212
 
        col.set_resizable(False)
213
 
        col.pack_start(cell, expand=True)
214
 
        col.add_attribute(cell, "text", LINE_NUM_COL)
215
 
        tv.append_column(col)
216
 
 
217
 
        cell = gtk.CellRendererText()
218
 
        cell.set_property("ypad", 0)
219
 
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
220
 
        cell.set_property("cell-background-gdk",
221
 
                          self.get_style().bg[gtk.STATE_NORMAL])
222
 
        col = gtk.TreeViewColumn("Committer")
223
 
        col.set_resizable(True)
224
 
        col.pack_start(cell, expand=True)
225
 
        col.add_attribute(cell, "text", COMMITTER_COL)
226
 
        tv.append_column(col)
227
 
 
228
 
        cell = gtk.CellRendererText()
229
 
        cell.set_property("xalign", 1.0)
230
 
        cell.set_property("ypad", 0)
231
 
        cell.set_property("cell-background-gdk",
232
 
                          self.get_style().bg[gtk.STATE_NORMAL])
233
 
        col = gtk.TreeViewColumn("Revno")
234
 
        col.set_resizable(False)
235
 
        col.pack_start(cell, expand=True)
236
 
        col.add_attribute(cell, "markup", REVNO_COL)
237
 
        tv.append_column(col)
238
 
 
239
 
        cell = gtk.CellRendererText()
240
 
        cell.set_property("ypad", 0)
241
 
        cell.set_property("family", "Monospace")
242
 
        col = gtk.TreeViewColumn()
243
 
        col.set_resizable(False)
244
 
        col.pack_start(cell, expand=True)
245
 
#        col.add_attribute(cell, "foreground", HIGHLIGHT_COLOR_COL)
246
 
        col.add_attribute(cell, "background", HIGHLIGHT_COLOR_COL)
247
 
        col.add_attribute(cell, "text", TEXT_LINE_COL)
248
 
        tv.append_column(col)
249
 
 
250
 
        tv.set_search_column(LINE_NUM_COL)
251
 
        
252
 
        return tv
253
 
 
254
 
    def _create_span_selector(self):
255
 
        ss = SpanSelector()
256
 
        ss.connect("span-changed", self._span_changed_cb)
257
 
        ss.show()
258
 
 
259
 
        return ss
260
 
 
261
 
    def _create_log_view(self):
262
 
        lv = LogView()
263
 
        lv.show()
264
 
 
265
 
        return lv
266
 
 
267
 
    def _create_button_box(self):
268
 
        box = gtk.HButtonBox()
269
 
        box.set_layout(gtk.BUTTONBOX_END)
270
 
        box.show()
271
 
        
272
 
        button = gtk.Button()
273
 
        button.set_use_stock(True)
274
 
        button.set_label("gtk-close")
275
 
        button.connect("clicked", lambda w: self.destroy())
276
 
        button.show()
277
 
        
278
 
        box.pack_start(button, expand=False, fill=False)
279
 
 
280
 
        return box
281
 
 
282
 
 
283
 
class NoneRevision:
284
 
    """ A fake revision.
285
 
 
286
 
    For when a revision is referenced but not present.
287
 
    """
288
 
 
289
 
    def __init__(self, revision_id):
290
 
        self.revision_id = revision_id
291
 
        self.parent_ids = []
292
 
        self.committer = "?"
293
 
        self.message = "?"
294
 
        self.timestamp = 0.0
295
 
        self.timezone = 0
296
 
 
297
 
 
298
 
class RevisionCache(object):
299
 
    """A caching revision source"""
300
 
    def __init__(self, real_source):
301
 
        self.__real_source = real_source
302
 
        self.__cache = {}
303
 
 
304
 
    def get_revision(self, revision_id):
305
 
        if revision_id not in self.__cache:
306
 
            revision = self.__real_source.get_revision(revision_id)
307
 
            self.__cache[revision_id] = revision
308
 
        return self.__cache[revision_id]