/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
0.1.1 by Dan Loda
First working version of xannotate.
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
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
17
import time
18
0.1.1 by Dan Loda
First working version of xannotate.
19
import pygtk
20
pygtk.require("2.0")
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
21
import gobject
0.1.1 by Dan Loda
First working version of xannotate.
22
import gtk
23
import pango
24
25
from bzrlib.errors import NoSuchRevision
26
0.1.18 by Aaron Bentley
Switched to using pink backgrounds
27
from colormap import AnnotateColorMap, AnnotateColorSaturation
0.1.1 by Dan Loda
First working version of xannotate.
28
from logview import LogView
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
29
from spanselector import SpanSelector
0.1.1 by Dan Loda
First working version of xannotate.
30
31
32
(
33
    REVISION_ID_COL,
34
    LINE_NUM_COL,
35
    COMMITTER_COL,
36
    REVNO_COL,
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
37
    HIGHLIGHT_COLOR_COL,
0.1.1 by Dan Loda
First working version of xannotate.
38
    TEXT_LINE_COL
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
39
) = range(6)
0.1.1 by Dan Loda
First working version of xannotate.
40
41
0.1.2 by Dan Loda
Rename plugin: xannotate => gannotate
42
class GAnnotateWindow(gtk.Window):
0.1.1 by Dan Loda
First working version of xannotate.
43
    """Annotate window."""
44
0.2.6 by Dan Loda
--plain option to disable highlighting. And update README
45
    def __init__(self, all=False, plain=False):
46
        self.all = all
47
        self.plain = plain
48
        
0.1.1 by Dan Loda
First working version of xannotate.
49
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
0.2.6 by Dan Loda
--plain option to disable highlighting. And update README
50
        
0.1.1 by Dan Loda
First working version of xannotate.
51
        self.set_icon(self.render_icon(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON))
0.1.18 by Aaron Bentley
Switched to using pink backgrounds
52
        self.annotate_colormap = AnnotateColorSaturation()
0.1.1 by Dan Loda
First working version of xannotate.
53
54
        self._create()
55
0.1.17 by Dan Loda
A little refactoring.
56
        if self.plain:
57
            self.span_selector.hide()
58
0.2.6 by Dan Loda
--plain option to disable highlighting. And update README
59
    def annotate(self, branch, file_id):
0.1.1 by Dan Loda
First working version of xannotate.
60
        self.revisions = {}
61
        
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
62
        # [revision id, line number, committer, revno, highlight color, line]
0.1.17 by Dan Loda
A little refactoring.
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)
0.1.1 by Dan Loda
First working version of xannotate.
69
        
70
        last_seen = None
0.4.1 by Aaron Bentley
Updated performance, API use
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()
0.2.6 by Dan Loda
--plain option to disable highlighting. And update README
101
0.1.17 by Dan Loda
A little refactoring.
102
        self.annoview.set_model(self.annomodel)
103
        self.annoview.grab_focus()
0.1.1 by Dan Loda
First working version of xannotate.
104
0.1.12 by Dan Loda
New --line option. Can now jump to a specific line number.
105
    def jump_to_line(self, lineno):
0.1.17 by Dan Loda
A little refactoring.
106
        if lineno > len(self.annomodel) or lineno < 1:
0.1.12 by Dan Loda
New --line option. Can now jump to a specific line number.
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
0.1.17 by Dan Loda
A little refactoring.
115
        self.annoview.set_cursor(row)
0.1.12 by Dan Loda
New --line option. Can now jump to a specific line number.
116
0.1.1 by Dan Loda
First working version of xannotate.
117
    def _annotate(self, branch, file_id):
118
        rev_hist = branch.revision_history()
0.1.20 by Aaron Bentley
Updated for api changes
119
        repository = branch.repository
120
        rev_tree = repository.revision_tree(branch.last_revision())
0.1.1 by Dan Loda
First working version of xannotate.
121
        rev_id = rev_tree.inventory[file_id].revision
0.1.20 by Aaron Bentley
Updated for api changes
122
        weave = repository.weave_store.get_weave(file_id,
123
                                                 branch.get_transaction())
0.1.1 by Dan Loda
First working version of xannotate.
124
        
0.1.23 by Aaron Bentley
Added revision caching to initial annotation
125
        revision_cache = RevisionCache(repository)
0.1.1 by Dan Loda
First working version of xannotate.
126
        for origin, text in weave.annotate_iter(rev_id):
0.4.1 by Aaron Bentley
Updated performance, API use
127
            rev_id = origin
0.1.1 by Dan Loda
First working version of xannotate.
128
            try:
0.1.23 by Aaron Bentley
Added revision caching to initial annotation
129
                revision = revision_cache.get_revision(rev_id)
0.1.1 by Dan Loda
First working version of xannotate.
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
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
140
    def _set_oldest_newest(self):
0.2.3 by Dan Loda
Initial support for custom highlight spans.
141
        rev_dates = map(lambda i: self.revisions[i].timestamp, self.revisions)
0.2.2 by Dan Loda
Add file's age as default span
142
        oldest = min(rev_dates)
0.2.3 by Dan Loda
Initial support for custom highlight spans.
143
        newest = max(rev_dates)
144
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
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))
0.2.2 by Dan Loda
Add file's age as default span
153
    
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
154
    def _span_changed_cb(self, w, span):
0.2.5 by Dan Loda
Use granny-like colors as default and rename ColorMap => AnnotateColorMap.
155
        self.annotate_colormap.set_span(span)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
156
        now = time.time()
0.1.17 by Dan Loda
A little refactoring.
157
        self.annomodel.foreach(self._highlight_annotation, now)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
158
0.2.2 by Dan Loda
Add file's age as default span
159
    def _highlight_annotation(self, model, path, iter, now):
160
        revision_id, = model.get(iter, REVISION_ID_COL)
161
        revision = self.revisions[revision_id]
0.2.5 by Dan Loda
Use granny-like colors as default and rename ColorMap => AnnotateColorMap.
162
        model.set(iter, HIGHLIGHT_COLOR_COL,
0.1.19 by Aaron Bentley
Different colours for different committers
163
                  self.annotate_colormap.get_color(revision, now))
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
164
0.1.1 by Dan Loda
First working version of xannotate.
165
    def _show_log(self, w):
0.1.17 by Dan Loda
A little refactoring.
166
        (path, col) = self.annoview.get_cursor()
167
        rev_id = self.annomodel[path][REVISION_ID_COL]
0.1.1 by Dan Loda
First working version of xannotate.
168
        self.logview.set_revision(self.revisions[rev_id])
169
170
    def _create(self):
0.1.17 by Dan Loda
A little refactoring.
171
        self.logview = self._create_log_view()
172
        self.annoview = self._create_annotate_view()
173
        self.span_selector = self._create_span_selector()
174
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
175
        vbox = gtk.VBox(False, 12)
176
        vbox.set_border_width(12)
177
        vbox.show()
0.1.17 by Dan Loda
A little refactoring.
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()
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
184
        
0.1.8 by Dan Loda
Remember window state. This introduces the gannotate.conf configuration file,
185
        self.pane = pane = gtk.VPaned()
0.1.17 by Dan Loda
A little refactoring.
186
        pane.add1(sw)
187
        pane.add2(self.logview)
0.1.1 by Dan Loda
First working version of xannotate.
188
        pane.show()
189
        vbox.pack_start(pane, expand=True, fill=True)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
190
        
191
        hbox = gtk.HBox(True, 6)
0.1.17 by Dan Loda
A little refactoring.
192
        hbox.pack_start(self.span_selector, expand=False, fill=True)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
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
0.1.1 by Dan Loda
First working version of xannotate.
197
        self.add(vbox)
198
199
    def _create_annotate_view(self):
0.1.17 by Dan Loda
A little refactoring.
200
        tv = gtk.TreeView()
201
        tv.set_rules_hint(False)
202
        tv.connect("cursor-changed", self._show_log)
203
        tv.show()
0.1.1 by Dan Loda
First working version of xannotate.
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",
0.1.17 by Dan Loda
A little refactoring.
210
                          tv.get_style().bg[gtk.STATE_NORMAL])
0.1.1 by Dan Loda
First working version of xannotate.
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)
0.1.17 by Dan Loda
A little refactoring.
215
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
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)
0.1.17 by Dan Loda
A little refactoring.
226
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
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)
0.1.17 by Dan Loda
A little refactoring.
237
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
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)
0.1.18 by Aaron Bentley
Switched to using pink backgrounds
245
#        col.add_attribute(cell, "foreground", HIGHLIGHT_COLOR_COL)
246
        col.add_attribute(cell, "background", HIGHLIGHT_COLOR_COL)
0.1.1 by Dan Loda
First working version of xannotate.
247
        col.add_attribute(cell, "text", TEXT_LINE_COL)
0.1.17 by Dan Loda
A little refactoring.
248
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
249
0.1.17 by Dan Loda
A little refactoring.
250
        tv.set_search_column(LINE_NUM_COL)
0.1.1 by Dan Loda
First working version of xannotate.
251
        
0.1.17 by Dan Loda
A little refactoring.
252
        return tv
0.1.1 by Dan Loda
First working version of xannotate.
253
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
254
    def _create_span_selector(self):
0.1.17 by Dan Loda
A little refactoring.
255
        ss = SpanSelector()
256
        ss.connect("span-changed", self._span_changed_cb)
257
        ss.show()
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
258
0.1.17 by Dan Loda
A little refactoring.
259
        return ss
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
260
0.1.1 by Dan Loda
First working version of xannotate.
261
    def _create_log_view(self):
0.1.17 by Dan Loda
A little refactoring.
262
        lv = LogView()
263
        lv.show()
264
265
        return lv
0.1.1 by Dan Loda
First working version of xannotate.
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)
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
279
0.1.1 by Dan Loda
First working version of xannotate.
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
0.1.23 by Aaron Bentley
Added revision caching to initial annotation
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]