/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
65 by Aaron Bentley
Handle first revision properly
26
from bzrlib.revision import NULL_REVISION
0.1.1 by Dan Loda
First working version of xannotate.
27
0.1.18 by Aaron Bentley
Switched to using pink backgrounds
28
from colormap import AnnotateColorMap, AnnotateColorSaturation
0.1.1 by Dan Loda
First working version of xannotate.
29
from logview import LogView
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
30
from spanselector import SpanSelector
0.1.1 by Dan Loda
First working version of xannotate.
31
32
33
(
34
    REVISION_ID_COL,
35
    LINE_NUM_COL,
36
    COMMITTER_COL,
37
    REVNO_COL,
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
38
    HIGHLIGHT_COLOR_COL,
0.1.1 by Dan Loda
First working version of xannotate.
39
    TEXT_LINE_COL
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
40
) = range(6)
0.1.1 by Dan Loda
First working version of xannotate.
41
42
0.1.2 by Dan Loda
Rename plugin: xannotate => gannotate
43
class GAnnotateWindow(gtk.Window):
0.1.1 by Dan Loda
First working version of xannotate.
44
    """Annotate window."""
45
0.2.6 by Dan Loda
--plain option to disable highlighting. And update README
46
    def __init__(self, all=False, plain=False):
47
        self.all = all
48
        self.plain = plain
49
        
0.1.1 by Dan Loda
First working version of xannotate.
50
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
0.2.6 by Dan Loda
--plain option to disable highlighting. And update README
51
        
0.1.1 by Dan Loda
First working version of xannotate.
52
        self.set_icon(self.render_icon(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON))
0.1.18 by Aaron Bentley
Switched to using pink backgrounds
53
        self.annotate_colormap = AnnotateColorSaturation()
0.1.1 by Dan Loda
First working version of xannotate.
54
55
        self._create()
56
0.1.17 by Dan Loda
A little refactoring.
57
        if self.plain:
58
            self.span_selector.hide()
59
0.2.6 by Dan Loda
--plain option to disable highlighting. And update README
60
    def annotate(self, branch, file_id):
0.1.1 by Dan Loda
First working version of xannotate.
61
        self.revisions = {}
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
62
        self.annotations = []
63
        self.branch = branch
59.2.3 by Aaron Bentley
Gannotate-launched diffs now jump to correct file
64
        self.file_id = file_id
0.1.1 by Dan Loda
First working version of xannotate.
65
        
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
66
        # [revision id, line number, committer, revno, highlight color, line]
0.1.17 by Dan Loda
A little refactoring.
67
        self.annomodel = gtk.ListStore(gobject.TYPE_STRING,
68
                                       gobject.TYPE_STRING,
69
                                       gobject.TYPE_STRING,
70
                                       gobject.TYPE_STRING,
71
                                       gobject.TYPE_STRING,
72
                                       gobject.TYPE_STRING)
0.1.1 by Dan Loda
First working version of xannotate.
73
        
74
        last_seen = None
0.4.1 by Aaron Bentley
Updated performance, API use
75
        try:
76
            branch.lock_read()
77
            branch.repository.lock_read()
78
            for line_no, (revision, revno, line)\
79
                    in enumerate(self._annotate(branch, file_id)):
80
                if revision.revision_id == last_seen and not self.all:
81
                    revno = committer = ""
82
                else:
83
                    last_seen = revision.revision_id
84
                    committer = revision.committer
85
86
                if revision.revision_id not in self.revisions:
87
                    self.revisions[revision.revision_id] = revision
88
89
                self.annomodel.append([revision.revision_id,
90
                                       line_no + 1,
91
                                       committer,
92
                                       revno,
93
                                       None,
94
                                       line.rstrip("\r\n")
95
                                      ])
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
96
                self.annotations.append(revision)
0.4.1 by Aaron Bentley
Updated performance, API use
97
98
            if not self.plain:
99
                self._set_oldest_newest()
100
                # Recall that calling activate_default will emit "span-changed",
101
                # so self._span_changed_cb will take care of initial highlighting
102
                self.span_selector.activate_default()
103
        finally:
104
            branch.repository.unlock()
105
            branch.unlock()
0.2.6 by Dan Loda
--plain option to disable highlighting. And update README
106
0.1.17 by Dan Loda
A little refactoring.
107
        self.annoview.set_model(self.annomodel)
108
        self.annoview.grab_focus()
0.1.1 by Dan Loda
First working version of xannotate.
109
0.1.12 by Dan Loda
New --line option. Can now jump to a specific line number.
110
    def jump_to_line(self, lineno):
0.1.17 by Dan Loda
A little refactoring.
111
        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.
112
            row = 0
113
            # FIXME:should really deal with this in the gui. Perhaps a status
114
            # bar?
115
            print("gannotate: Line number %d does't exist. Defaulting to "
116
                  "line 1." % lineno)
79 by Jelmer Vernooij
Handle empty files more gracefully. Fixes #58951.
117
	    return
0.1.12 by Dan Loda
New --line option. Can now jump to a specific line number.
118
        else:
119
            row = lineno - 1
120
0.1.17 by Dan Loda
A little refactoring.
121
        self.annoview.set_cursor(row)
59.2.1 by Aaron Bentley
Gannotate takes a line number
122
        self.annoview.scroll_to_cell(row, use_align=True)
0.1.12 by Dan Loda
New --line option. Can now jump to a specific line number.
123
0.1.1 by Dan Loda
First working version of xannotate.
124
    def _annotate(self, branch, file_id):
125
        rev_hist = branch.revision_history()
0.1.20 by Aaron Bentley
Updated for api changes
126
        repository = branch.repository
127
        rev_tree = repository.revision_tree(branch.last_revision())
0.1.1 by Dan Loda
First working version of xannotate.
128
        rev_id = rev_tree.inventory[file_id].revision
0.1.20 by Aaron Bentley
Updated for api changes
129
        weave = repository.weave_store.get_weave(file_id,
130
                                                 branch.get_transaction())
0.1.1 by Dan Loda
First working version of xannotate.
131
        
0.1.23 by Aaron Bentley
Added revision caching to initial annotation
132
        revision_cache = RevisionCache(repository)
0.1.1 by Dan Loda
First working version of xannotate.
133
        for origin, text in weave.annotate_iter(rev_id):
0.4.1 by Aaron Bentley
Updated performance, API use
134
            rev_id = origin
0.1.1 by Dan Loda
First working version of xannotate.
135
            try:
0.1.23 by Aaron Bentley
Added revision caching to initial annotation
136
                revision = revision_cache.get_revision(rev_id)
0.1.1 by Dan Loda
First working version of xannotate.
137
                if rev_id in rev_hist:
138
                    revno = branch.revision_id_to_revno(rev_id)
139
                else:
140
                    revno = "merge"
141
            except NoSuchRevision:
142
                revision = NoneRevision(rev_id)
143
                revno = "?"
144
145
            yield revision, revno, text
146
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
147
    def _set_oldest_newest(self):
0.2.3 by Dan Loda
Initial support for custom highlight spans.
148
        rev_dates = map(lambda i: self.revisions[i].timestamp, self.revisions)
79 by Jelmer Vernooij
Handle empty files more gracefully. Fixes #58951.
149
        if len(rev_dates) == 0:
150
            return
0.2.2 by Dan Loda
Add file's age as default span
151
        oldest = min(rev_dates)
0.2.3 by Dan Loda
Initial support for custom highlight spans.
152
        newest = max(rev_dates)
153
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
154
        span = self._span_from_seconds(time.time() - oldest)
155
        self.span_selector.set_to_oldest_span(span)
156
        
157
        span = self._span_from_seconds(newest - oldest)
158
        self.span_selector.set_newest_to_oldest_span(span)
159
160
    def _span_from_seconds(self, seconds):
161
        return (seconds / (24 * 60 * 60))
0.2.2 by Dan Loda
Add file's age as default span
162
    
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
163
    def _span_changed_cb(self, w, span):
0.2.5 by Dan Loda
Use granny-like colors as default and rename ColorMap => AnnotateColorMap.
164
        self.annotate_colormap.set_span(span)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
165
        now = time.time()
0.1.17 by Dan Loda
A little refactoring.
166
        self.annomodel.foreach(self._highlight_annotation, now)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
167
0.2.2 by Dan Loda
Add file's age as default span
168
    def _highlight_annotation(self, model, path, iter, now):
169
        revision_id, = model.get(iter, REVISION_ID_COL)
170
        revision = self.revisions[revision_id]
0.2.5 by Dan Loda
Use granny-like colors as default and rename ColorMap => AnnotateColorMap.
171
        model.set(iter, HIGHLIGHT_COLOR_COL,
0.1.19 by Aaron Bentley
Different colours for different committers
172
                  self.annotate_colormap.get_color(revision, now))
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
173
0.1.1 by Dan Loda
First working version of xannotate.
174
    def _show_log(self, w):
0.1.17 by Dan Loda
A little refactoring.
175
        (path, col) = self.annoview.get_cursor()
79 by Jelmer Vernooij
Handle empty files more gracefully. Fixes #58951.
176
        if path is None:
177
            return
0.1.17 by Dan Loda
A little refactoring.
178
        rev_id = self.annomodel[path][REVISION_ID_COL]
0.1.1 by Dan Loda
First working version of xannotate.
179
        self.logview.set_revision(self.revisions[rev_id])
180
181
    def _create(self):
0.1.17 by Dan Loda
A little refactoring.
182
        self.logview = self._create_log_view()
183
        self.annoview = self._create_annotate_view()
184
        self.span_selector = self._create_span_selector()
185
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
186
        vbox = gtk.VBox(False, 12)
187
        vbox.set_border_width(12)
188
        vbox.show()
0.1.17 by Dan Loda
A little refactoring.
189
190
        sw = gtk.ScrolledWindow()
191
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
192
        sw.set_shadow_type(gtk.SHADOW_IN)
193
        sw.add(self.annoview)
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
194
        self.annoview.gwindow = self
0.1.17 by Dan Loda
A little refactoring.
195
        sw.show()
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
196
        
0.1.8 by Dan Loda
Remember window state. This introduces the gannotate.conf configuration file,
197
        self.pane = pane = gtk.VPaned()
0.1.17 by Dan Loda
A little refactoring.
198
        pane.add1(sw)
199
        pane.add2(self.logview)
0.1.1 by Dan Loda
First working version of xannotate.
200
        pane.show()
201
        vbox.pack_start(pane, expand=True, fill=True)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
202
        
203
        hbox = gtk.HBox(True, 6)
0.1.17 by Dan Loda
A little refactoring.
204
        hbox.pack_start(self.span_selector, expand=False, fill=True)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
205
        hbox.pack_start(self._create_button_box(), expand=False, fill=True)
206
        hbox.show()
207
        vbox.pack_start(hbox, expand=False, fill=True)
208
0.1.1 by Dan Loda
First working version of xannotate.
209
        self.add(vbox)
210
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
211
    def row_diff(self, tv, path, tvc):
212
        row = path[0]
213
        revision = self.annotations[row]
65 by Aaron Bentley
Handle first revision properly
214
        repository = self.branch.repository
215
        tree1 = repository.revision_tree(revision.revision_id)
216
        if len(revision.parent_ids) > 0:
217
            tree2 = repository.revision_tree(revision.parent_ids[0])
218
        else:
219
            tree2 = repository.revision_tree(NULL_REVISION)
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
220
        from bzrlib.plugins.gtk.viz.diffwin import DiffWindow
221
        window = DiffWindow()
66 by Aaron Bentley
Fix annotate diff window title
222
        window.set_diff("Diff for row %d" % (row+1), tree1, tree2)
59.2.3 by Aaron Bentley
Gannotate-launched diffs now jump to correct file
223
        window.set_file(tree1.id2path(self.file_id))
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
224
        window.show()
225
226
0.1.1 by Dan Loda
First working version of xannotate.
227
    def _create_annotate_view(self):
0.1.17 by Dan Loda
A little refactoring.
228
        tv = gtk.TreeView()
229
        tv.set_rules_hint(False)
230
        tv.connect("cursor-changed", self._show_log)
231
        tv.show()
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
232
        tv.connect("row-activated", self.row_diff)
0.1.1 by Dan Loda
First working version of xannotate.
233
234
        cell = gtk.CellRendererText()
235
        cell.set_property("xalign", 1.0)
236
        cell.set_property("ypad", 0)
237
        cell.set_property("family", "Monospace")
238
        cell.set_property("cell-background-gdk",
0.1.17 by Dan Loda
A little refactoring.
239
                          tv.get_style().bg[gtk.STATE_NORMAL])
0.1.1 by Dan Loda
First working version of xannotate.
240
        col = gtk.TreeViewColumn()
241
        col.set_resizable(False)
242
        col.pack_start(cell, expand=True)
243
        col.add_attribute(cell, "text", LINE_NUM_COL)
0.1.17 by Dan Loda
A little refactoring.
244
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
245
246
        cell = gtk.CellRendererText()
247
        cell.set_property("ypad", 0)
248
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
249
        cell.set_property("cell-background-gdk",
250
                          self.get_style().bg[gtk.STATE_NORMAL])
251
        col = gtk.TreeViewColumn("Committer")
252
        col.set_resizable(True)
253
        col.pack_start(cell, expand=True)
254
        col.add_attribute(cell, "text", COMMITTER_COL)
0.1.17 by Dan Loda
A little refactoring.
255
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
256
257
        cell = gtk.CellRendererText()
258
        cell.set_property("xalign", 1.0)
259
        cell.set_property("ypad", 0)
260
        cell.set_property("cell-background-gdk",
261
                          self.get_style().bg[gtk.STATE_NORMAL])
262
        col = gtk.TreeViewColumn("Revno")
263
        col.set_resizable(False)
264
        col.pack_start(cell, expand=True)
265
        col.add_attribute(cell, "markup", REVNO_COL)
0.1.17 by Dan Loda
A little refactoring.
266
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
267
268
        cell = gtk.CellRendererText()
269
        cell.set_property("ypad", 0)
270
        cell.set_property("family", "Monospace")
271
        col = gtk.TreeViewColumn()
272
        col.set_resizable(False)
273
        col.pack_start(cell, expand=True)
0.1.18 by Aaron Bentley
Switched to using pink backgrounds
274
#        col.add_attribute(cell, "foreground", HIGHLIGHT_COLOR_COL)
275
        col.add_attribute(cell, "background", HIGHLIGHT_COLOR_COL)
0.1.1 by Dan Loda
First working version of xannotate.
276
        col.add_attribute(cell, "text", TEXT_LINE_COL)
0.1.17 by Dan Loda
A little refactoring.
277
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
278
0.1.17 by Dan Loda
A little refactoring.
279
        tv.set_search_column(LINE_NUM_COL)
0.1.1 by Dan Loda
First working version of xannotate.
280
        
0.1.17 by Dan Loda
A little refactoring.
281
        return tv
0.1.1 by Dan Loda
First working version of xannotate.
282
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
283
    def _create_span_selector(self):
0.1.17 by Dan Loda
A little refactoring.
284
        ss = SpanSelector()
285
        ss.connect("span-changed", self._span_changed_cb)
286
        ss.show()
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
287
0.1.17 by Dan Loda
A little refactoring.
288
        return ss
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
289
0.1.1 by Dan Loda
First working version of xannotate.
290
    def _create_log_view(self):
0.1.17 by Dan Loda
A little refactoring.
291
        lv = LogView()
292
        lv.show()
293
294
        return lv
0.1.1 by Dan Loda
First working version of xannotate.
295
296
    def _create_button_box(self):
297
        box = gtk.HButtonBox()
298
        box.set_layout(gtk.BUTTONBOX_END)
299
        box.show()
300
        
301
        button = gtk.Button()
302
        button.set_use_stock(True)
303
        button.set_label("gtk-close")
304
        button.connect("clicked", lambda w: self.destroy())
305
        button.show()
306
        
307
        box.pack_start(button, expand=False, fill=False)
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
308
0.1.1 by Dan Loda
First working version of xannotate.
309
        return box
310
311
312
class NoneRevision:
313
    """ A fake revision.
314
315
    For when a revision is referenced but not present.
316
    """
317
318
    def __init__(self, revision_id):
319
        self.revision_id = revision_id
320
        self.parent_ids = []
321
        self.committer = "?"
322
        self.message = "?"
323
        self.timestamp = 0.0
324
        self.timezone = 0
325
0.1.23 by Aaron Bentley
Added revision caching to initial annotation
326
327
class RevisionCache(object):
328
    """A caching revision source"""
329
    def __init__(self, real_source):
330
        self.__real_source = real_source
331
        self.__cache = {}
332
333
    def get_revision(self, revision_id):
334
        if revision_id not in self.__cache:
335
            revision = self.__real_source.get_revision(revision_id)
336
            self.__cache[revision_id] = revision
337
        return self.__cache[revision_id]