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
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)
449
453
self.__cache[revision_id] = revision
450
454
return self.__cache[revision_id]
452
class SearchBox(gtk.HBox):
456
class SearchBox(Gtk.HBox):
453
457
"""A button box for searching in text or lines of annotations"""
454
458
def __init__(self):
455
gtk.HBox.__init__(self, False, 6)
459
Gtk.HBox.__init__(self, homogeneous=False, spacing=6)
458
button = gtk.Button()
460
image.set_from_stock('gtk-stop', gtk.ICON_SIZE_BUTTON)
462
button = Gtk.Button()
464
image.set_from_stock('gtk-stop', Gtk.IconSize.BUTTON)
461
465
button.set_image(image)
462
button.set_relief(gtk.RELIEF_NONE)
466
button.set_relief(Gtk.ReliefStyle.NONE)
463
467
button.connect("clicked", lambda w: self.hide_all())
464
self.pack_start(button, expand=False, fill=False)
468
self.pack_start(button, False, False, 0)
468
472
self._label = label
469
self.pack_start(label, expand=False, fill=False)
473
self.pack_start(label, False, False, 0)
472
476
self._entry = entry
473
477
entry.connect("activate", lambda w, d: self._do_search(d),
475
self.pack_start(entry, expand=False, fill=False)
479
self.pack_start(entry, False, False, 0)
477
481
# Next/previous buttons
478
button = gtk.Button('_Next')
480
image.set_from_stock('gtk-go-forward', gtk.ICON_SIZE_BUTTON)
482
button = Gtk.Button('_Next')
484
image.set_from_stock('gtk-go-forward', Gtk.IconSize.BUTTON)
481
485
button.set_image(image)
482
486
button.connect("clicked", lambda w, d: self._do_search(d),
484
self.pack_start(button, expand=False, fill=False)
488
self.pack_start(button, False, False, 0)
486
button = gtk.Button('_Previous')
488
image.set_from_stock('gtk-go-back', gtk.ICON_SIZE_BUTTON)
490
button = Gtk.Button('_Previous')
492
image.set_from_stock('gtk-go-back', Gtk.IconSize.BUTTON)
489
493
button.set_image(image)
490
494
button.connect("clicked", lambda w, d: self._do_search(d),
492
self.pack_start(button, expand=False, fill=False)
496
self.pack_start(button, False, False, 0)
495
check = gtk.CheckButton('Match case')
499
check = Gtk.CheckButton('Match case')
496
500
self._match_case = check
497
self.pack_start(check, expand=False, fill=False)
501
self.pack_start(check, False, False, 0)
499
check = gtk.CheckButton('Regexp')
503
check = Gtk.CheckButton('Regexp')
500
504
check.connect("toggled", lambda w: self._set_label())
501
505
self._regexp = check
502
self.pack_start(check, expand=False, fill=False)
506
self.pack_start(check, False, False, 0)
504
508
self._view = None
505
509
self._column = None
533
537
def _match(self, model, iterator, column):
534
538
matching_case = self._match_case.get_active()
535
string, = model.get(iterator, column)
539
cell_value, = model.get(iterator, column)
536
540
key = self._entry.get_text()
537
if self._regexp.get_active():
541
if column == LINE_NUM_COL:
542
# FIXME: For goto-line there are faster algorithms than searching
543
# every line til we find the right one! -- mbp 2011-01-27
544
return key.strip() == str(cell_value)
545
elif self._regexp.get_active():
538
546
if matching_case:
539
match = re.compile(key).search(string, 1)
547
match = re.compile(key).search(cell_value, 1)
541
match = re.compile(key, re.I).search(string, 1)
549
match = re.compile(key, re.I).search(cell_value, 1)
543
551
if not matching_case:
544
string = string.lower()
552
cell_value = cell_value.lower()
545
553
key = key.lower()
546
match = string.find(key) != -1
554
match = cell_value.find(key) != -1