/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)
117
        else:
118
            row = lineno - 1
119
0.1.17 by Dan Loda
A little refactoring.
120
        self.annoview.set_cursor(row)
59.2.1 by Aaron Bentley
Gannotate takes a line number
121
        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.
122
0.1.1 by Dan Loda
First working version of xannotate.
123
    def _annotate(self, branch, file_id):
124
        rev_hist = branch.revision_history()
0.1.20 by Aaron Bentley
Updated for api changes
125
        repository = branch.repository
126
        rev_tree = repository.revision_tree(branch.last_revision())
0.1.1 by Dan Loda
First working version of xannotate.
127
        rev_id = rev_tree.inventory[file_id].revision
0.1.20 by Aaron Bentley
Updated for api changes
128
        weave = repository.weave_store.get_weave(file_id,
129
                                                 branch.get_transaction())
0.1.1 by Dan Loda
First working version of xannotate.
130
        
0.1.23 by Aaron Bentley
Added revision caching to initial annotation
131
        revision_cache = RevisionCache(repository)
0.1.1 by Dan Loda
First working version of xannotate.
132
        for origin, text in weave.annotate_iter(rev_id):
0.4.1 by Aaron Bentley
Updated performance, API use
133
            rev_id = origin
0.1.1 by Dan Loda
First working version of xannotate.
134
            try:
0.1.23 by Aaron Bentley
Added revision caching to initial annotation
135
                revision = revision_cache.get_revision(rev_id)
0.1.1 by Dan Loda
First working version of xannotate.
136
                if rev_id in rev_hist:
137
                    revno = branch.revision_id_to_revno(rev_id)
138
                else:
139
                    revno = "merge"
140
            except NoSuchRevision:
141
                revision = NoneRevision(rev_id)
142
                revno = "?"
143
144
            yield revision, revno, text
145
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
146
    def _set_oldest_newest(self):
0.2.3 by Dan Loda
Initial support for custom highlight spans.
147
        rev_dates = map(lambda i: self.revisions[i].timestamp, self.revisions)
0.2.2 by Dan Loda
Add file's age as default span
148
        oldest = min(rev_dates)
0.2.3 by Dan Loda
Initial support for custom highlight spans.
149
        newest = max(rev_dates)
150
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
151
        span = self._span_from_seconds(time.time() - oldest)
152
        self.span_selector.set_to_oldest_span(span)
153
        
154
        span = self._span_from_seconds(newest - oldest)
155
        self.span_selector.set_newest_to_oldest_span(span)
156
157
    def _span_from_seconds(self, seconds):
158
        return (seconds / (24 * 60 * 60))
0.2.2 by Dan Loda
Add file's age as default span
159
    
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
160
    def _span_changed_cb(self, w, span):
0.2.5 by Dan Loda
Use granny-like colors as default and rename ColorMap => AnnotateColorMap.
161
        self.annotate_colormap.set_span(span)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
162
        now = time.time()
0.1.17 by Dan Loda
A little refactoring.
163
        self.annomodel.foreach(self._highlight_annotation, now)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
164
0.2.2 by Dan Loda
Add file's age as default span
165
    def _highlight_annotation(self, model, path, iter, now):
166
        revision_id, = model.get(iter, REVISION_ID_COL)
167
        revision = self.revisions[revision_id]
0.2.5 by Dan Loda
Use granny-like colors as default and rename ColorMap => AnnotateColorMap.
168
        model.set(iter, HIGHLIGHT_COLOR_COL,
0.1.19 by Aaron Bentley
Different colours for different committers
169
                  self.annotate_colormap.get_color(revision, now))
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
170
0.1.1 by Dan Loda
First working version of xannotate.
171
    def _show_log(self, w):
0.1.17 by Dan Loda
A little refactoring.
172
        (path, col) = self.annoview.get_cursor()
173
        rev_id = self.annomodel[path][REVISION_ID_COL]
0.1.1 by Dan Loda
First working version of xannotate.
174
        self.logview.set_revision(self.revisions[rev_id])
175
176
    def _create(self):
0.1.17 by Dan Loda
A little refactoring.
177
        self.logview = self._create_log_view()
178
        self.annoview = self._create_annotate_view()
179
        self.span_selector = self._create_span_selector()
180
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
181
        vbox = gtk.VBox(False, 12)
182
        vbox.set_border_width(12)
183
        vbox.show()
0.1.17 by Dan Loda
A little refactoring.
184
185
        sw = gtk.ScrolledWindow()
186
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
187
        sw.set_shadow_type(gtk.SHADOW_IN)
188
        sw.add(self.annoview)
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
189
        self.annoview.gwindow = self
0.1.17 by Dan Loda
A little refactoring.
190
        sw.show()
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
191
        
0.1.8 by Dan Loda
Remember window state. This introduces the gannotate.conf configuration file,
192
        self.pane = pane = gtk.VPaned()
0.1.17 by Dan Loda
A little refactoring.
193
        pane.add1(sw)
194
        pane.add2(self.logview)
0.1.1 by Dan Loda
First working version of xannotate.
195
        pane.show()
196
        vbox.pack_start(pane, expand=True, fill=True)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
197
        
198
        hbox = gtk.HBox(True, 6)
0.1.17 by Dan Loda
A little refactoring.
199
        hbox.pack_start(self.span_selector, expand=False, fill=True)
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
200
        hbox.pack_start(self._create_button_box(), expand=False, fill=True)
201
        hbox.show()
202
        vbox.pack_start(hbox, expand=False, fill=True)
203
0.1.1 by Dan Loda
First working version of xannotate.
204
        self.add(vbox)
205
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
206
    def row_diff(self, tv, path, tvc):
207
        row = path[0]
208
        revision = self.annotations[row]
65 by Aaron Bentley
Handle first revision properly
209
        repository = self.branch.repository
210
        tree1 = repository.revision_tree(revision.revision_id)
211
        if len(revision.parent_ids) > 0:
212
            tree2 = repository.revision_tree(revision.parent_ids[0])
213
        else:
214
            tree2 = repository.revision_tree(NULL_REVISION)
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
215
        from bzrlib.plugins.gtk.viz.diffwin import DiffWindow
216
        window = DiffWindow()
66 by Aaron Bentley
Fix annotate diff window title
217
        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
218
        window.set_file(tree1.id2path(self.file_id))
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
219
        window.show()
220
221
0.1.1 by Dan Loda
First working version of xannotate.
222
    def _create_annotate_view(self):
0.1.17 by Dan Loda
A little refactoring.
223
        tv = gtk.TreeView()
224
        tv.set_rules_hint(False)
225
        tv.connect("cursor-changed", self._show_log)
226
        tv.show()
59.2.2 by Aaron Bentley
Annotate launches a diff for the particular revision
227
        tv.connect("row-activated", self.row_diff)
0.1.1 by Dan Loda
First working version of xannotate.
228
229
        cell = gtk.CellRendererText()
230
        cell.set_property("xalign", 1.0)
231
        cell.set_property("ypad", 0)
232
        cell.set_property("family", "Monospace")
233
        cell.set_property("cell-background-gdk",
0.1.17 by Dan Loda
A little refactoring.
234
                          tv.get_style().bg[gtk.STATE_NORMAL])
0.1.1 by Dan Loda
First working version of xannotate.
235
        col = gtk.TreeViewColumn()
236
        col.set_resizable(False)
237
        col.pack_start(cell, expand=True)
238
        col.add_attribute(cell, "text", LINE_NUM_COL)
0.1.17 by Dan Loda
A little refactoring.
239
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
240
241
        cell = gtk.CellRendererText()
242
        cell.set_property("ypad", 0)
243
        cell.set_property("ellipsize", pango.ELLIPSIZE_END)
244
        cell.set_property("cell-background-gdk",
245
                          self.get_style().bg[gtk.STATE_NORMAL])
246
        col = gtk.TreeViewColumn("Committer")
247
        col.set_resizable(True)
248
        col.pack_start(cell, expand=True)
249
        col.add_attribute(cell, "text", COMMITTER_COL)
0.1.17 by Dan Loda
A little refactoring.
250
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
251
252
        cell = gtk.CellRendererText()
253
        cell.set_property("xalign", 1.0)
254
        cell.set_property("ypad", 0)
255
        cell.set_property("cell-background-gdk",
256
                          self.get_style().bg[gtk.STATE_NORMAL])
257
        col = gtk.TreeViewColumn("Revno")
258
        col.set_resizable(False)
259
        col.pack_start(cell, expand=True)
260
        col.add_attribute(cell, "markup", REVNO_COL)
0.1.17 by Dan Loda
A little refactoring.
261
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
262
263
        cell = gtk.CellRendererText()
264
        cell.set_property("ypad", 0)
265
        cell.set_property("family", "Monospace")
266
        col = gtk.TreeViewColumn()
267
        col.set_resizable(False)
268
        col.pack_start(cell, expand=True)
0.1.18 by Aaron Bentley
Switched to using pink backgrounds
269
#        col.add_attribute(cell, "foreground", HIGHLIGHT_COLOR_COL)
270
        col.add_attribute(cell, "background", HIGHLIGHT_COLOR_COL)
0.1.1 by Dan Loda
First working version of xannotate.
271
        col.add_attribute(cell, "text", TEXT_LINE_COL)
0.1.17 by Dan Loda
A little refactoring.
272
        tv.append_column(col)
0.1.1 by Dan Loda
First working version of xannotate.
273
0.1.17 by Dan Loda
A little refactoring.
274
        tv.set_search_column(LINE_NUM_COL)
0.1.1 by Dan Loda
First working version of xannotate.
275
        
0.1.17 by Dan Loda
A little refactoring.
276
        return tv
0.1.1 by Dan Loda
First working version of xannotate.
277
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
278
    def _create_span_selector(self):
0.1.17 by Dan Loda
A little refactoring.
279
        ss = SpanSelector()
280
        ss.connect("span-changed", self._span_changed_cb)
281
        ss.show()
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
282
0.1.17 by Dan Loda
A little refactoring.
283
        return ss
0.2.1 by Dan Loda
first go at emacs vc-annotate like highlighting
284
0.1.1 by Dan Loda
First working version of xannotate.
285
    def _create_log_view(self):
0.1.17 by Dan Loda
A little refactoring.
286
        lv = LogView()
287
        lv.show()
288
289
        return lv
0.1.1 by Dan Loda
First working version of xannotate.
290
291
    def _create_button_box(self):
292
        box = gtk.HButtonBox()
293
        box.set_layout(gtk.BUTTONBOX_END)
294
        box.show()
295
        
296
        button = gtk.Button()
297
        button.set_use_stock(True)
298
        button.set_label("gtk-close")
299
        button.connect("clicked", lambda w: self.destroy())
300
        button.show()
301
        
302
        box.pack_start(button, expand=False, fill=False)
0.2.4 by Dan Loda
Extract class SpanSelector out of GAnnotateWindow.
303
0.1.1 by Dan Loda
First working version of xannotate.
304
        return box
305
306
307
class NoneRevision:
308
    """ A fake revision.
309
310
    For when a revision is referenced but not present.
311
    """
312
313
    def __init__(self, revision_id):
314
        self.revision_id = revision_id
315
        self.parent_ids = []
316
        self.committer = "?"
317
        self.message = "?"
318
        self.timestamp = 0.0
319
        self.timezone = 0
320
0.1.23 by Aaron Bentley
Added revision caching to initial annotation
321
322
class RevisionCache(object):
323
    """A caching revision source"""
324
    def __init__(self, real_source):
325
        self.__real_source = real_source
326
        self.__cache = {}
327
328
    def get_revision(self, revision_id):
329
        if revision_id not in self.__cache:
330
            revision = self.__real_source.get_revision(revision_id)
331
            self.__cache[revision_id] = revision
332
        return self.__cache[revision_id]