/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: Szilveszter Farkas (Phanatic)
  • Date: 2007-03-15 12:43:48 UTC
  • mto: (126.1.38 bzr-gtk)
  • mto: This revision was merged to the branch mainline in revision 172.
  • Revision ID: szilveszter.farkas@gmail.com-20070315124348-0nx0zb7fv4pa8xk6
Fix the indentation error in the TortoiseBZR test.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
import pango
24
24
import re
25
25
 
26
 
from bzrlib import patiencediff
 
26
from bzrlib import patiencediff, tsort
27
27
from bzrlib.errors import NoSuchRevision
28
28
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
29
29
 
30
 
from colormap import AnnotateColorSaturation
31
 
from bzrlib.plugins.gtk.revisionview import RevisionView
32
 
from bzrlib.plugins.gtk.window import Window
 
30
from colormap import AnnotateColorMap, AnnotateColorSaturation
 
31
from bzrlib.plugins.gtk.logview import LogView
33
32
 
34
33
 
35
34
(
42
41
) = range(6)
43
42
 
44
43
 
45
 
class GAnnotateWindow(Window):
 
44
class GAnnotateWindow(gtk.Window):
46
45
    """Annotate window."""
47
46
 
48
 
    def __init__(self, all=False, plain=False, parent=None, branch=None):
 
47
    def __init__(self, all=False, plain=False):
49
48
        self.all = all
50
49
        self.plain = plain
51
 
        self._branch = branch
52
 
 
53
 
        Window.__init__(self, parent)
54
 
 
 
50
        
 
51
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
 
52
        
55
53
        self.set_icon(self.render_icon(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON))
56
54
        self.annotate_colormap = AnnotateColorSaturation()
57
55
 
58
56
        self._create()
59
57
        self.revisions = {}
60
 
        self.history = []
61
 
        self._no_back = set()
62
58
 
63
59
    def annotate(self, tree, branch, file_id):
64
60
        self.annotations = []
65
61
        self.branch = branch
66
62
        self.tree = tree
67
63
        self.file_id = file_id
68
 
        self.revisionview.set_file_id(file_id)
69
64
        self.revision_id = getattr(tree, 'get_revision_id', 
70
65
                                   lambda: CURRENT_REVISION)()
71
 
 
72
 
        # [revision id, line number, author, revno, highlight color, line]
 
66
        
 
67
        # [revision id, line number, committer, revno, highlight color, line]
73
68
        self.annomodel = gtk.ListStore(gobject.TYPE_STRING,
74
 
                                       gobject.TYPE_INT,
 
69
                                       gobject.TYPE_STRING,
75
70
                                       gobject.TYPE_STRING,
76
71
                                       gobject.TYPE_STRING,
77
72
                                       gobject.TYPE_STRING,
78
73
                                       gobject.TYPE_STRING)
79
 
 
 
74
        
80
75
        last_seen = None
81
76
        try:
82
77
            branch.lock_read()
83
78
            branch.repository.lock_read()
84
 
            self.dotted = {}
85
 
            revno_map = self.branch.get_revision_id_to_revno_map()
86
 
            for revision_id, revno in revno_map.iteritems():
87
 
                self.dotted[revision_id] = '.'.join(str(num) for num in revno)
88
79
            for line_no, (revision, revno, line)\
89
 
                in enumerate(self._annotate(tree, file_id)):
 
80
                    in enumerate(self._annotate(tree, file_id)):
90
81
                if revision.revision_id == last_seen and not self.all:
91
 
                    revno = author = ""
 
82
                    revno = committer = ""
92
83
                else:
93
84
                    last_seen = revision.revision_id
94
 
                    author = ", ".join(revision.get_apparent_authors())
 
85
                    committer = revision.committer
95
86
 
96
87
                if revision.revision_id not in self.revisions:
97
88
                    self.revisions[revision.revision_id] = revision
98
89
 
99
90
                self.annomodel.append([revision.revision_id,
100
91
                                       line_no + 1,
101
 
                                       author,
 
92
                                       committer,
102
93
                                       revno,
103
94
                                       None,
104
95
                                       line.rstrip("\r\n")
105
 
                                       ])
 
96
                                      ])
106
97
                self.annotations.append(revision)
107
98
 
108
99
            if not self.plain:
114
105
 
115
106
        self.annoview.set_model(self.annomodel)
116
107
        self.annoview.grab_focus()
117
 
        my_revno = self.dotted.get(self.revision_id, 'current')
118
 
        title = '%s (%s) - gannotate' % (self.tree.id2path(file_id), my_revno)
119
 
        self.set_title(title)
120
108
 
121
109
    def jump_to_line(self, lineno):
122
110
        if lineno > len(self.annomodel) or lineno < 1:
125
113
            # bar?
126
114
            print("gannotate: Line number %d does't exist. Defaulting to "
127
115
                  "line 1." % lineno)
128
 
            return
 
116
            return
129
117
        else:
130
118
            row = lineno - 1
131
119
 
132
120
        self.annoview.set_cursor(row)
133
121
        self.annoview.scroll_to_cell(row, use_align=True)
134
122
 
 
123
    def _dotted_revnos(self, repository, revision_id):
 
124
        """Return a dict of revision_id -> dotted revno
 
125
        
 
126
        :param repository: The repository to get the graph from
 
127
        :param revision_id: The last revision for which this info is needed
 
128
        """
 
129
        graph = repository.get_revision_graph(revision_id)
 
130
        dotted = {}
 
131
        for n, revision_id, d, revno, e in tsort.merge_sort(graph, 
 
132
            revision_id, generate_revno=True):
 
133
            dotted[revision_id] = '.'.join(str(num) for num in revno)
 
134
        return dotted
135
135
 
136
136
    def _annotate(self, tree, file_id):
137
137
        current_revision = FakeRevision(CURRENT_REVISION)
139
139
        current_revision.timestamp = time.time()
140
140
        current_revision.message = '[Not yet committed]'
141
141
        current_revision.parent_ids = tree.get_parent_ids()
142
 
        current_revision.properties['branch-nick'] = self.branch._get_nick(local=True)
 
142
        current_revision.properties['branch-nick'] = self.branch.nick
143
143
        current_revno = '%d?' % (self.branch.revno() + 1)
144
144
        repository = self.branch.repository
145
145
        if self.revision_id == CURRENT_REVISION:
146
146
            revision_id = self.branch.last_revision()
147
147
        else:
148
148
            revision_id = self.revision_id
 
149
        dotted = self._dotted_revnos(repository, revision_id)
149
150
        revision_cache = RevisionCache(repository, self.revisions)
150
151
        for origin, text in tree.annotate_iter(file_id):
151
152
            rev_id = origin
155
156
            else:
156
157
                try:
157
158
                    revision = revision_cache.get_revision(rev_id)
158
 
                    revno = self.dotted.get(rev_id, 'merge')
 
159
                    revno = dotted.get(rev_id, 'merge')
159
160
                    if len(revno) > 15:
160
161
                        revno = 'merge'
161
162
                except NoSuchRevision:
176
177
            return None
177
178
        return self.annomodel[path][REVISION_ID_COL]
178
179
 
179
 
    def _activate_selected_revision(self, w):
 
180
    def _show_log(self, w):
180
181
        rev_id = self._selected_revision()
181
 
        if not rev_id or rev_id == NULL_REVISION:
 
182
        if rev_id is None:
182
183
            return
183
 
        selected = self.revisions[rev_id]
184
 
        self.revisionview.set_revision(selected)
185
 
        if (len(selected.parent_ids) != 0 and selected.parent_ids[0] not in
186
 
            self._no_back):
187
 
            enable_back = True
188
 
        else:
189
 
            enable_back = False
190
 
        self.back_button.set_sensitive(enable_back)
 
184
        self.logview.set_revision(self.revisions[rev_id])
191
185
 
192
186
    def _create(self):
193
 
        self.revisionview = self._create_log_view()
 
187
        self.logview = self._create_log_view()
194
188
        self.annoview = self._create_annotate_view()
195
189
 
196
 
        vbox = gtk.VBox(False)
 
190
        vbox = gtk.VBox(False, 12)
 
191
        vbox.set_border_width(12)
197
192
        vbox.show()
198
193
 
199
194
        sw = gtk.ScrolledWindow()
202
197
        sw.add(self.annoview)
203
198
        self.annoview.gwindow = self
204
199
        sw.show()
205
 
 
206
 
        swbox = gtk.VBox()
207
 
        swbox.pack_start(sw)
208
 
        swbox.show()
209
 
 
210
 
        hbox = gtk.HBox(False, 6)
211
 
        self.back_button = self._create_back_button()
212
 
        hbox.pack_start(self.back_button, expand=False, fill=True)
213
 
        self.forward_button = self._create_forward_button()
214
 
        hbox.pack_start(self.forward_button, expand=False, fill=True)
215
 
        self.find_button = self._create_find_button()
216
 
        hbox.pack_start(self.find_button, expand=False, fill=True)
217
 
        self.goto_button = self._create_goto_button()
218
 
        hbox.pack_start(self.goto_button, expand=False, fill=True)
219
 
        hbox.show()
220
 
        vbox.pack_start(hbox, expand=False, fill=True)
221
 
 
 
200
        
222
201
        self.pane = pane = gtk.VPaned()
223
 
        pane.add1(swbox)
224
 
        pane.add2(self.revisionview)
 
202
        pane.add1(sw)
 
203
        pane.add2(self.logview)
225
204
        pane.show()
226
205
        vbox.pack_start(pane, expand=True, fill=True)
227
206
 
228
207
        self._search = SearchBox()
229
 
        swbox.pack_start(self._search, expand=False, fill=True)
 
208
        vbox.pack_start(self._search, expand=False, fill=True)
230
209
        accels = gtk.AccelGroup()
231
210
        accels.connect_group(gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
232
211
                             gtk.ACCEL_LOCKED,
236
215
                             self._search_by_line)
237
216
        self.add_accel_group(accels)
238
217
 
 
218
        hbox = gtk.HBox(True, 6)
 
219
        hbox.pack_start(self._create_prev_button(), expand=False, fill=True)
 
220
        hbox.pack_end(self._create_button_box(), expand=False, fill=True)
 
221
        hbox.show()
 
222
        vbox.pack_start(hbox, expand=False, fill=True)
 
223
 
239
224
        self.add(vbox)
240
225
 
241
 
    def _search_by_text(self, *ignored): # (accel_group, window, key, modifiers):
 
226
    def _search_by_text(self, accel_group, window, key, modifiers):
242
227
        self._search.show_for('text')
243
228
        self._search.set_target(self.annoview, TEXT_LINE_COL)
244
229
 
245
 
    def _search_by_line(self, *ignored): # accel_group, window, key, modifiers):
 
230
    def _search_by_line(self, accel_group, window, key, modifiers):
246
231
        self._search.show_for('line')
247
232
        self._search.set_target(self.annoview, LINE_NUM_COL)
248
233
 
249
 
    def line_diff(self, tv, path, tvc):
 
234
    def row_diff(self, tv, path, tvc):
250
235
        row = path[0]
251
236
        revision = self.annotations[row]
252
237
        repository = self.branch.repository
260
245
            else:
261
246
                tree2 = repository.revision_tree(NULL_REVISION)
262
247
        from bzrlib.plugins.gtk.diff import DiffWindow
263
 
        window = DiffWindow(self)
264
 
        window.set_diff("Diff for line %d" % (row+1), tree1, tree2)
 
248
        window = DiffWindow()
 
249
        window.set_diff("Diff for row %d" % (row+1), tree1, tree2)
265
250
        window.set_file(tree1.id2path(self.file_id))
266
251
        window.show()
267
252
 
269
254
    def _create_annotate_view(self):
270
255
        tv = gtk.TreeView()
271
256
        tv.set_rules_hint(False)
272
 
        tv.connect("cursor-changed", self._activate_selected_revision)
 
257
        tv.connect("cursor-changed", self._show_log)
273
258
        tv.show()
274
 
        tv.connect("row-activated", self.line_diff)
 
259
        tv.connect("row-activated", self.row_diff)
275
260
 
276
261
        cell = gtk.CellRendererText()
277
262
        cell.set_property("xalign", 1.0)
325
310
        return tv
326
311
 
327
312
    def _create_log_view(self):
328
 
        lv = RevisionView(self._branch)
 
313
        lv = LogView()
329
314
        lv.show()
330
315
        return lv
331
316
 
332
 
    def _create_back_button(self):
 
317
    def _create_button_box(self):
 
318
        box = gtk.HButtonBox()
 
319
        box.set_layout(gtk.BUTTONBOX_END)
 
320
        box.show()
 
321
 
 
322
        button = gtk.Button()
 
323
        button.set_use_stock(True)
 
324
        button.set_label("gtk-close")
 
325
        button.connect("clicked", lambda w: self.destroy())
 
326
        button.show()
 
327
 
 
328
        box.pack_start(button, expand=False, fill=False)
 
329
 
 
330
        return box
 
331
 
 
332
    def _create_prev_button(self):
 
333
        box = gtk.HButtonBox()
 
334
        box.set_layout(gtk.BUTTONBOX_START)
 
335
        box.show()
 
336
        
333
337
        button = gtk.Button()
334
338
        button.set_use_stock(True)
335
339
        button.set_label("gtk-go-back")
336
340
        button.connect("clicked", lambda w: self.go_back())
337
 
        button.set_relief(gtk.RELIEF_NONE)
338
 
        button.show()
339
 
        return button
340
 
 
341
 
    def _create_forward_button(self):
342
 
        button = gtk.Button()
343
 
        button.set_use_stock(True)
344
 
        button.set_label("gtk-go-forward")
345
 
        button.connect("clicked", lambda w: self.go_forward())
346
 
        button.set_relief(gtk.RELIEF_NONE)
347
 
        button.show()
348
 
        button.set_sensitive(False)
349
 
        return button
350
 
 
351
 
    def _create_find_button(self):
352
 
        button = gtk.Button()
353
 
        button.set_use_stock(True)
354
 
        button.set_label("gtk-find")
355
 
        button.set_tooltip_text("Search for text (Ctrl+F)")
356
 
        button.connect("clicked", self._search_by_text)
357
 
        button.set_relief(gtk.RELIEF_NONE)
358
 
        button.show()
359
 
        button.set_sensitive(True)
360
 
        return button
361
 
 
362
 
    def _create_goto_button(self):
363
 
        button = gtk.Button()
364
 
        button.set_label("Goto Line")
365
 
        button.set_tooltip_text("Scroll to a line by entering its number (Ctrl+G)")
366
 
        button.connect("clicked", self._search_by_line)
367
 
        button.set_relief(gtk.RELIEF_NONE)
368
 
        button.show()
369
 
        button.set_sensitive(True)
370
 
        return button
 
341
        button.show()
 
342
        box.pack_start(button, expand=False, fill=False)
 
343
        return box
371
344
 
372
345
    def go_back(self):
373
 
        last_tree = self.tree
374
346
        rev_id = self._selected_revision()
375
347
        parent_id = self.revisions[rev_id].parent_ids[0]
376
 
        target_tree = self.branch.repository.revision_tree(parent_id)
377
 
        if self._go(target_tree):
378
 
            self.history.append(last_tree)
379
 
            self.forward_button.set_sensitive(True)
380
 
        else:
381
 
            self._no_back.add(parent_id)
382
 
            self.back_button.set_sensitive(False)
383
 
 
384
 
    def go_forward(self):
385
 
        if len(self.history) == 0:
386
 
            return
387
 
        target_tree = self.history.pop()
388
 
        if len(self.history) == 0:
389
 
            self.forward_button.set_sensitive(False)
390
 
        self._go(target_tree)
391
 
 
392
 
    def _go(self, target_tree):
393
 
        rev_id = self._selected_revision()
394
 
        if self.file_id in target_tree:
395
 
            offset = self.get_scroll_offset(target_tree)
 
348
        tree = self.branch.repository.revision_tree(parent_id)
 
349
        if self.file_id in tree:
 
350
            offset = self.get_scroll_offset(tree)
396
351
            (row,), col = self.annoview.get_cursor()
397
 
            self.annotate(target_tree, self.branch, self.file_id)
398
 
            new_row = row+offset
399
 
            if new_row < 0:
400
 
                new_row = 0
401
 
            self.annoview.set_cursor(new_row)
402
 
            return True
403
 
        else:
404
 
            return False
 
352
            self.annotate(tree, self.branch, self.file_id)
 
353
            self.annoview.set_cursor(row+offset)
405
354
 
406
355
    def get_scroll_offset(self, tree):
407
356
        old = self.tree.get_file(self.file_id)
414
363
                return j - i
415
364
 
416
365
 
417
 
class FakeRevision(object):
 
366
 
 
367
class FakeRevision:
418
368
    """ A fake revision.
419
369
 
420
370
    For when a revision is referenced but not present.
429
379
        self.timezone = 0
430
380
        self.properties = {}
431
381
 
432
 
    def get_apparent_authors(self):
433
 
        return [self.committer]
434
 
 
435
382
 
436
383
class RevisionCache(object):
437
384
    """A caching revision source"""
438
 
 
439
385
    def __init__(self, real_source, seed_cache=None):
440
386
        self.__real_source = real_source
441
387
        if seed_cache is None:
532
478
 
533
479
    def _match(self, model, iterator, column):
534
480
        matching_case = self._match_case.get_active()
535
 
        cell_value, = model.get(iterator, column)
 
481
        string, = model.get(iterator, column)
536
482
        key = self._entry.get_text()
537
 
        if column == LINE_NUM_COL:
538
 
            # FIXME: For goto-line there are faster algorithms than searching 
539
 
            # every line til we find the right one! -- mbp 2011-01-27
540
 
            return key.strip() == str(cell_value)
541
 
        elif self._regexp.get_active():
 
483
        if self._regexp.get_active():
542
484
            if matching_case:
543
 
                match = re.compile(key).search(cell_value, 1)
 
485
                match = re.compile(key).search(string, 1)
544
486
            else:
545
 
                match = re.compile(key, re.I).search(cell_value, 1)
 
487
                match = re.compile(key, re.I).search(string, 1)
546
488
        else:
547
489
            if not matching_case:
548
 
                cell_value = cell_value.lower()
 
490
                string = string.lower()
549
491
                key = key.lower()
550
 
            match = cell_value.find(key) != -1
 
492
            match = string.find(key) != -1
551
493
 
552
494
        return match
553
495