19
from gi.repository import GObject
20
from gi.repository import Gdk
21
from gi.repository import Gtk
22
from gi.repository import Pango
26
from bzrlib import patiencediff, tsort
25
from bzrlib import patiencediff
27
26
from bzrlib.errors import NoSuchRevision
28
27
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
30
from colormap import AnnotateColorMap, AnnotateColorSaturation
29
from bzrlib.plugins.gtk.annotate.colormap import AnnotateColorSaturation
30
from bzrlib.plugins.gtk.i18n import _i18n
31
31
from bzrlib.plugins.gtk.revisionview import RevisionView
32
32
from bzrlib.plugins.gtk.window import Window
167
168
def _highlight_annotation(self, model, path, iter, now):
168
169
revision_id, = model.get(iter, REVISION_ID_COL)
169
170
revision = self.revisions[revision_id]
170
model.set(iter, HIGHLIGHT_COLOR_COL,
171
self.annotate_colormap.get_color(revision, now))
171
# XXX sinzui 2011-08-12: What does get_color return?
172
color = self.annotate_colormap.get_color(revision, now)
173
model.set_value(iter, HIGHLIGHT_COLOR_COL, color)
173
175
def _selected_revision(self):
174
176
(path, col) = self.annoview.get_cursor()
193
195
self.revisionview = self._create_log_view()
194
196
self.annoview = self._create_annotate_view()
196
vbox = gtk.VBox(False)
198
vbox = Gtk.VBox(homogeneous=False, spacing=0)
199
sw = gtk.ScrolledWindow()
200
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
201
sw.set_shadow_type(gtk.SHADOW_IN)
201
sw = Gtk.ScrolledWindow()
202
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
203
sw.set_shadow_type(Gtk.ShadowType.IN)
202
204
sw.add(self.annoview)
203
205
self.annoview.gwindow = self
209
swbox.pack_start(sw, True, True, 0)
210
hbox = gtk.HBox(False, 6)
212
hbox = Gtk.HBox(homogeneous=False, spacing=6)
211
213
self.back_button = self._create_back_button()
212
hbox.pack_start(self.back_button, expand=False, fill=True)
214
hbox.pack_start(self.back_button, False, True, 0)
213
215
self.forward_button = self._create_forward_button()
214
hbox.pack_start(self.forward_button, expand=False, fill=True)
216
hbox.pack_start(self.forward_button, False, True, 0)
215
217
self.find_button = self._create_find_button()
216
hbox.pack_start(self.find_button, expand=False, fill=True)
218
hbox.pack_start(self.find_button, False, True, 0)
217
219
self.goto_button = self._create_goto_button()
218
hbox.pack_start(self.goto_button, expand=False, fill=True)
220
hbox.pack_start(self.goto_button, False, True, 0)
220
vbox.pack_start(hbox, expand=False, fill=True)
222
self.pane = pane = gtk.VPaned()
222
vbox.pack_start(hbox, False, True, 0)
224
self.pane = pane = Gtk.Paned.new(Gtk.Orientation.VERTICAL)
224
226
pane.add2(self.revisionview)
226
vbox.pack_start(pane, expand=True, fill=True)
228
vbox.pack_start(pane, True, True, 0)
228
230
self._search = SearchBox()
229
swbox.pack_start(self._search, expand=False, fill=True)
230
accels = gtk.AccelGroup()
231
accels.connect_group(gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
231
swbox.pack_start(self._search, False, True, 0)
232
accels = Gtk.AccelGroup()
233
accels.connect(Gdk.KEY_f, Gdk.ModifierType.CONTROL_MASK,
234
Gtk.AccelFlags.LOCKED,
233
235
self._search_by_text)
234
accels.connect_group(gtk.keysyms.g, gtk.gdk.CONTROL_MASK,
236
accels.connect(Gdk.KEY_g, Gdk.ModifierType.CONTROL_MASK,
237
Gtk.AccelFlags.LOCKED,
236
238
self._search_by_line)
237
239
self.add_accel_group(accels)
261
263
tree2 = repository.revision_tree(NULL_REVISION)
262
264
from bzrlib.plugins.gtk.diff import DiffWindow
263
window = DiffWindow()
265
window = DiffWindow(self)
264
266
window.set_diff("Diff for line %d" % (row+1), tree1, tree2)
265
267
window.set_file(tree1.id2path(self.file_id))
269
271
def _create_annotate_view(self):
271
273
tv.set_rules_hint(False)
272
274
tv.connect("cursor-changed", self._activate_selected_revision)
274
276
tv.connect("row-activated", self.line_diff)
276
cell = gtk.CellRendererText()
278
cell = Gtk.CellRendererText()
277
279
cell.set_property("xalign", 1.0)
278
280
cell.set_property("ypad", 0)
279
281
cell.set_property("family", "Monospace")
280
282
cell.set_property("cell-background-gdk",
281
tv.get_style().bg[gtk.STATE_NORMAL])
282
col = gtk.TreeViewColumn()
283
tv.get_style().bg[Gtk.StateType.NORMAL])
284
col = Gtk.TreeViewColumn()
283
285
col.set_resizable(False)
284
col.pack_start(cell, expand=True)
286
col.pack_start(cell, True)
285
287
col.add_attribute(cell, "text", LINE_NUM_COL)
286
288
tv.append_column(col)
288
cell = gtk.CellRendererText()
290
cell = Gtk.CellRendererText()
289
291
cell.set_property("ypad", 0)
290
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
292
cell.set_property("ellipsize", Pango.EllipsizeMode.END)
291
293
cell.set_property("cell-background-gdk",
292
self.get_style().bg[gtk.STATE_NORMAL])
293
col = gtk.TreeViewColumn("Committer")
294
self.get_style().bg[Gtk.StateType.NORMAL])
295
col = Gtk.TreeViewColumn("Committer")
294
296
col.set_resizable(True)
295
col.pack_start(cell, expand=True)
297
col.pack_start(cell, True)
296
298
col.add_attribute(cell, "text", COMMITTER_COL)
297
299
tv.append_column(col)
299
cell = gtk.CellRendererText()
301
cell = Gtk.CellRendererText()
300
302
cell.set_property("xalign", 1.0)
301
303
cell.set_property("ypad", 0)
302
304
cell.set_property("cell-background-gdk",
303
self.get_style().bg[gtk.STATE_NORMAL])
304
col = gtk.TreeViewColumn("Revno")
305
self.get_style().bg[Gtk.StateType.NORMAL])
306
col = Gtk.TreeViewColumn("Revno")
305
307
col.set_resizable(False)
306
col.pack_start(cell, expand=True)
308
col.pack_start(cell, True)
307
309
col.add_attribute(cell, "markup", REVNO_COL)
308
310
tv.append_column(col)
310
cell = gtk.CellRendererText()
312
cell = Gtk.CellRendererText()
311
313
cell.set_property("ypad", 0)
312
314
cell.set_property("family", "Monospace")
313
col = gtk.TreeViewColumn()
315
col = Gtk.TreeViewColumn()
314
316
col.set_resizable(False)
315
col.pack_start(cell, expand=True)
317
col.pack_start(cell, True)
316
318
# col.add_attribute(cell, "foreground", HIGHLIGHT_COLOR_COL)
317
319
col.add_attribute(cell, "background", HIGHLIGHT_COLOR_COL)
318
320
col.add_attribute(cell, "text", TEXT_LINE_COL)
319
321
tv.append_column(col)
321
# FIXME: Now that C-f is now used for search by text we
322
# may as well disable the auto search.
323
tv.set_search_column(LINE_NUM_COL)
323
# interactive substring search
324
def search_equal_func(model, column, key, iter):
325
return model.get_value(iter, TEXT_LINE_COL).lower().find(key.lower()) == -1
327
tv.set_enable_search(True)
328
tv.set_search_equal_func(search_equal_func, None)
332
337
def _create_back_button(self):
333
button = gtk.Button()
338
button = Gtk.Button()
334
339
button.set_use_stock(True)
335
340
button.set_label("gtk-go-back")
336
341
button.connect("clicked", lambda w: self.go_back())
337
button.set_relief(gtk.RELIEF_NONE)
342
button.set_relief(Gtk.ReliefStyle.NONE)
341
346
def _create_forward_button(self):
342
button = gtk.Button()
347
button = Gtk.Button()
343
348
button.set_use_stock(True)
344
349
button.set_label("gtk-go-forward")
345
350
button.connect("clicked", lambda w: self.go_forward())
346
button.set_relief(gtk.RELIEF_NONE)
351
button.set_relief(Gtk.ReliefStyle.NONE)
348
353
button.set_sensitive(False)
351
356
def _create_find_button(self):
352
button = gtk.Button()
357
button = Gtk.Button()
353
358
button.set_use_stock(True)
354
359
button.set_label("gtk-find")
355
360
button.set_tooltip_text("Search for text (Ctrl+F)")
356
361
button.connect("clicked", self._search_by_text)
357
button.set_relief(gtk.RELIEF_NONE)
362
button.set_relief(Gtk.ReliefStyle.NONE)
359
364
button.set_sensitive(True)
362
367
def _create_goto_button(self):
363
button = gtk.Button()
368
button = Gtk.Button()
364
369
button.set_label("Goto Line")
365
370
button.set_tooltip_text("Scroll to a line by entering its number (Ctrl+G)")
366
371
button.connect("clicked", self._search_by_line)
367
button.set_relief(gtk.RELIEF_NONE)
372
button.set_relief(Gtk.ReliefStyle.NONE)
369
374
button.set_sensitive(True)
393
398
rev_id = self._selected_revision()
394
399
if self.file_id in target_tree:
395
400
offset = self.get_scroll_offset(target_tree)
396
(row,), col = self.annoview.get_cursor()
401
path, col = self.annoview.get_cursor()
402
(row,) = path.get_indices()
397
403
self.annotate(target_tree, self.branch, self.file_id)
398
404
new_row = row+offset
401
self.annoview.set_cursor(new_row)
407
new_path = Gtk.TreePath(path=new_row)
408
self.annoview.set_cursor(new_path, None, False)
449
457
self.__cache[revision_id] = revision
450
458
return self.__cache[revision_id]
452
class SearchBox(gtk.HBox):
461
class SearchBox(Gtk.HBox):
453
462
"""A button box for searching in text or lines of annotations"""
454
463
def __init__(self):
455
gtk.HBox.__init__(self, False, 6)
464
super(SearchBox, self).__init__(homogeneous=False, spacing=6)
458
button = gtk.Button()
460
image.set_from_stock('gtk-stop', gtk.ICON_SIZE_BUTTON)
467
button = Gtk.Button()
469
image.set_from_stock('gtk-stop', Gtk.IconSize.BUTTON)
461
470
button.set_image(image)
462
button.set_relief(gtk.RELIEF_NONE)
463
button.connect("clicked", lambda w: self.hide_all())
464
self.pack_start(button, expand=False, fill=False)
471
button.set_relief(Gtk.ReliefStyle.NONE)
472
button.connect("clicked", lambda w: self.hide())
473
self.pack_start(button, False, False, 0)
468
477
self._label = label
469
self.pack_start(label, expand=False, fill=False)
478
self.pack_start(label, False, False, 0)
472
481
self._entry = entry
473
482
entry.connect("activate", lambda w, d: self._do_search(d),
475
self.pack_start(entry, expand=False, fill=False)
484
self.pack_start(entry, False, False, 0)
477
486
# Next/previous buttons
478
button = gtk.Button('_Next')
480
image.set_from_stock('gtk-go-forward', gtk.ICON_SIZE_BUTTON)
487
button = Gtk.Button(_i18n('_Next'), use_underline=True)
489
image.set_from_stock('gtk-go-forward', Gtk.IconSize.BUTTON)
481
490
button.set_image(image)
482
491
button.connect("clicked", lambda w, d: self._do_search(d),
484
self.pack_start(button, expand=False, fill=False)
493
self.pack_start(button, False, False, 0)
486
button = gtk.Button('_Previous')
488
image.set_from_stock('gtk-go-back', gtk.ICON_SIZE_BUTTON)
495
button = Gtk.Button(_i18n('_Previous'), use_underline=True)
497
image.set_from_stock('gtk-go-back', Gtk.IconSize.BUTTON)
489
498
button.set_image(image)
490
499
button.connect("clicked", lambda w, d: self._do_search(d),
492
self.pack_start(button, expand=False, fill=False)
501
self.pack_start(button, False, False, 0)
495
check = gtk.CheckButton('Match case')
504
check = Gtk.CheckButton('Match case')
496
505
self._match_case = check
497
self.pack_start(check, expand=False, fill=False)
506
self.pack_start(check, False, False, 0)
499
check = gtk.CheckButton('Regexp')
508
check = Gtk.CheckButton('Regexp')
500
509
check.connect("toggled", lambda w: self._set_label())
501
510
self._regexp = check
502
self.pack_start(check, expand=False, fill=False)
511
self.pack_start(check, False, False, 0)
504
513
self._view = None
505
514
self._column = None
533
542
def _match(self, model, iterator, column):
534
543
matching_case = self._match_case.get_active()
535
string, = model.get(iterator, column)
544
cell_value, = model.get(iterator, column)
536
545
key = self._entry.get_text()
537
if self._regexp.get_active():
546
if column == LINE_NUM_COL:
547
# FIXME: For goto-line there are faster algorithms than searching
548
# every line til we find the right one! -- mbp 2011-01-27
549
return key.strip() == str(cell_value)
550
elif self._regexp.get_active():
538
551
if matching_case:
539
match = re.compile(key).search(string, 1)
552
match = re.compile(key).search(cell_value, 1)
541
match = re.compile(key, re.I).search(string, 1)
554
match = re.compile(key, re.I).search(cell_value, 1)
543
556
if not matching_case:
544
string = string.lower()
557
cell_value = cell_value.lower()
545
558
key = key.lower()
546
match = string.find(key) != -1
559
match = cell_value.find(key) != -1