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