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
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
45
class GAnnotateWindow(Window):
44
class GAnnotateWindow(gtk.Window):
46
45
"""Annotate window."""
48
def __init__(self, all=False, plain=False, parent=None, branch=None):
47
def __init__(self, all=False, plain=False):
53
Window.__init__(self, parent)
51
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
55
53
self.set_icon(self.render_icon(gtk.STOCK_FIND, gtk.ICON_SIZE_BUTTON))
56
54
self.annotate_colormap = AnnotateColorSaturation()
59
57
self.revisions = {}
63
59
def annotate(self, tree, branch, file_id):
64
60
self.annotations = []
65
61
self.branch = branch
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)()
72
# [revision id, line number, author, revno, highlight color, line]
67
# [revision id, line number, committer, revno, highlight color, line]
73
68
self.annomodel = gtk.ListStore(gobject.TYPE_STRING,
75
70
gobject.TYPE_STRING,
76
71
gobject.TYPE_STRING,
77
72
gobject.TYPE_STRING,
78
73
gobject.TYPE_STRING)
83
78
branch.repository.lock_read()
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:
82
revno = committer = ""
93
84
last_seen = revision.revision_id
94
author = ", ".join(revision.get_apparent_authors())
85
committer = revision.committer
96
87
if revision.revision_id not in self.revisions:
97
88
self.revisions[revision.revision_id] = revision
99
90
self.annomodel.append([revision.revision_id,
104
95
line.rstrip("\r\n")
106
97
self.annotations.append(revision)
108
99
if not self.plain:
126
114
print("gannotate: Line number %d does't exist. Defaulting to "
127
115
"line 1." % lineno)
132
120
self.annoview.set_cursor(row)
133
121
self.annoview.scroll_to_cell(row, use_align=True)
123
def _dotted_revnos(self, repository, revision_id):
124
"""Return a dict of revision_id -> dotted revno
126
:param repository: The repository to get the graph from
127
:param revision_id: The last revision for which this info is needed
129
graph = repository.get_revision_graph(revision_id)
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)
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()
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):
177
178
return self.annomodel[path][REVISION_ID_COL]
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:
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
190
self.back_button.set_sensitive(enable_back)
184
self.logview.set_revision(self.revisions[rev_id])
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()
196
vbox = gtk.VBox(False)
190
vbox = gtk.VBox(False, 12)
191
vbox.set_border_width(12)
199
194
sw = gtk.ScrolledWindow()
202
197
sw.add(self.annoview)
203
198
self.annoview.gwindow = self
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)
220
vbox.pack_start(hbox, expand=False, fill=True)
222
201
self.pane = pane = gtk.VPaned()
224
pane.add2(self.revisionview)
203
pane.add2(self.logview)
226
205
vbox.pack_start(pane, expand=True, fill=True)
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)
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)
222
vbox.pack_start(hbox, expand=False, fill=True)
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)
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)
249
def line_diff(self, tv, path, tvc):
234
def row_diff(self, tv, path, tvc):
251
236
revision = self.annotations[row]
252
237
repository = self.branch.repository
327
312
def _create_log_view(self):
328
lv = RevisionView(self._branch)
332
def _create_back_button(self):
317
def _create_button_box(self):
318
box = gtk.HButtonBox()
319
box.set_layout(gtk.BUTTONBOX_END)
322
button = gtk.Button()
323
button.set_use_stock(True)
324
button.set_label("gtk-close")
325
button.connect("clicked", lambda w: self.destroy())
328
box.pack_start(button, expand=False, fill=False)
332
def _create_prev_button(self):
333
box = gtk.HButtonBox()
334
box.set_layout(gtk.BUTTONBOX_START)
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)
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)
348
button.set_sensitive(False)
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)
359
button.set_sensitive(True)
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)
369
button.set_sensitive(True)
342
box.pack_start(button, expand=False, fill=False)
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)
381
self._no_back.add(parent_id)
382
self.back_button.set_sensitive(False)
384
def go_forward(self):
385
if len(self.history) == 0:
387
target_tree = self.history.pop()
388
if len(self.history) == 0:
389
self.forward_button.set_sensitive(False)
390
self._go(target_tree)
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)
401
self.annoview.set_cursor(new_row)
352
self.annotate(tree, self.branch, self.file_id)
353
self.annoview.set_cursor(row+offset)
406
355
def get_scroll_offset(self, tree):
407
356
old = self.tree.get_file(self.file_id)
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)
545
match = re.compile(key, re.I).search(cell_value, 1)
487
match = re.compile(key, re.I).search(string, 1)
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