19
from gi.repository import GObject
20
from gi.repository import Gdk
21
from gi.repository import Gtk
22
from gi.repository import Pango
25
from bzrlib import patiencediff
26
from bzrlib import patiencediff, tsort
26
27
from bzrlib.errors import NoSuchRevision
27
28
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
29
from bzrlib.plugins.gtk.annotate.colormap import AnnotateColorSaturation
30
from bzrlib.plugins.gtk.i18n import _i18n
30
from colormap import AnnotateColorMap, AnnotateColorSaturation
31
31
from bzrlib.plugins.gtk.revisionview import RevisionView
32
32
from bzrlib.plugins.gtk.window import Window
168
167
def _highlight_annotation(self, model, path, iter, now):
169
168
revision_id, = model.get(iter, REVISION_ID_COL)
170
169
revision = self.revisions[revision_id]
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)
170
model.set(iter, HIGHLIGHT_COLOR_COL,
171
self.annotate_colormap.get_color(revision, now))
175
173
def _selected_revision(self):
176
174
(path, col) = self.annoview.get_cursor()
195
193
self.revisionview = self._create_log_view()
196
194
self.annoview = self._create_annotate_view()
198
vbox = Gtk.VBox(homogeneous=False, spacing=0)
196
vbox = gtk.VBox(False)
201
sw = Gtk.ScrolledWindow()
202
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
203
sw.set_shadow_type(Gtk.ShadowType.IN)
199
sw = gtk.ScrolledWindow()
200
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
201
sw.set_shadow_type(gtk.SHADOW_IN)
204
202
sw.add(self.annoview)
205
203
self.annoview.gwindow = self
209
swbox.pack_start(sw, True, True, 0)
212
hbox = Gtk.HBox(homogeneous=False, spacing=6)
210
hbox = gtk.HBox(False, 6)
213
211
self.back_button = self._create_back_button()
214
hbox.pack_start(self.back_button, False, True, 0)
212
hbox.pack_start(self.back_button, expand=False, fill=True)
215
213
self.forward_button = self._create_forward_button()
216
hbox.pack_start(self.forward_button, False, True, 0)
217
self.find_button = self._create_find_button()
218
hbox.pack_start(self.find_button, False, True, 0)
219
self.goto_button = self._create_goto_button()
220
hbox.pack_start(self.goto_button, False, True, 0)
214
hbox.pack_start(self.forward_button, expand=False, fill=True)
222
vbox.pack_start(hbox, False, True, 0)
224
self.pane = pane = Gtk.Paned.new(Gtk.Orientation.VERTICAL)
216
vbox.pack_start(hbox, expand=False, fill=True)
218
self.pane = pane = gtk.VPaned()
226
220
pane.add2(self.revisionview)
228
vbox.pack_start(pane, True, True, 0)
222
vbox.pack_start(pane, expand=True, fill=True)
230
224
self._search = SearchBox()
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,
225
swbox.pack_start(self._search, expand=False, fill=True)
226
accels = gtk.AccelGroup()
227
accels.connect_group(gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
235
229
self._search_by_text)
236
accels.connect(Gdk.KEY_g, Gdk.ModifierType.CONTROL_MASK,
237
Gtk.AccelFlags.LOCKED,
230
accels.connect_group(gtk.keysyms.g, gtk.gdk.CONTROL_MASK,
238
232
self._search_by_line)
239
233
self.add_accel_group(accels)
243
def _search_by_text(self, *ignored): # (accel_group, window, key, modifiers):
237
def _search_by_text(self, accel_group, window, key, modifiers):
244
238
self._search.show_for('text')
245
239
self._search.set_target(self.annoview, TEXT_LINE_COL)
247
def _search_by_line(self, *ignored): # accel_group, window, key, modifiers):
241
def _search_by_line(self, accel_group, window, key, modifiers):
248
242
self._search.show_for('line')
249
243
self._search.set_target(self.annoview, LINE_NUM_COL)
251
245
def line_diff(self, tv, path, tvc):
252
row = path.get_indices()[0]
253
247
revision = self.annotations[row]
254
248
repository = self.branch.repository
255
249
if revision.revision_id == CURRENT_REVISION:
263
257
tree2 = repository.revision_tree(NULL_REVISION)
264
258
from bzrlib.plugins.gtk.diff import DiffWindow
265
window = DiffWindow(self)
259
window = DiffWindow()
266
260
window.set_diff("Diff for line %d" % (row+1), tree1, tree2)
267
261
window.set_file(tree1.id2path(self.file_id))
271
265
def _create_annotate_view(self):
273
267
tv.set_rules_hint(False)
274
268
tv.connect("cursor-changed", self._activate_selected_revision)
276
270
tv.connect("row-activated", self.line_diff)
278
cell = Gtk.CellRendererText()
272
cell = gtk.CellRendererText()
279
273
cell.set_property("xalign", 1.0)
280
274
cell.set_property("ypad", 0)
281
275
cell.set_property("family", "Monospace")
283
"cell-background-rgba",
284
tv.get_style_context().get_background_color(Gtk.StateType.NORMAL))
285
col = Gtk.TreeViewColumn()
276
cell.set_property("cell-background-gdk",
277
tv.get_style().bg[gtk.STATE_NORMAL])
278
col = gtk.TreeViewColumn()
286
279
col.set_resizable(False)
287
col.pack_start(cell, True)
280
col.pack_start(cell, expand=True)
288
281
col.add_attribute(cell, "text", LINE_NUM_COL)
289
282
tv.append_column(col)
291
style_context = self.get_style_context()
293
cell = Gtk.CellRendererText()
284
cell = gtk.CellRendererText()
294
285
cell.set_property("ypad", 0)
295
cell.set_property("ellipsize", Pango.EllipsizeMode.END)
297
"cell-background-rgba",
298
style_context.get_background_color(Gtk.StateType.NORMAL))
299
col = Gtk.TreeViewColumn("Committer")
286
cell.set_property("ellipsize", pango.ELLIPSIZE_END)
287
cell.set_property("cell-background-gdk",
288
self.get_style().bg[gtk.STATE_NORMAL])
289
col = gtk.TreeViewColumn("Committer")
300
290
col.set_resizable(True)
301
col.pack_start(cell, True)
291
col.pack_start(cell, expand=True)
302
292
col.add_attribute(cell, "text", COMMITTER_COL)
303
293
tv.append_column(col)
305
cell = Gtk.CellRendererText()
295
cell = gtk.CellRendererText()
306
296
cell.set_property("xalign", 1.0)
307
297
cell.set_property("ypad", 0)
309
"cell-background-rgba",
310
style_context.get_background_color(Gtk.StateType.NORMAL))
311
col = Gtk.TreeViewColumn("Revno")
298
cell.set_property("cell-background-gdk",
299
self.get_style().bg[gtk.STATE_NORMAL])
300
col = gtk.TreeViewColumn("Revno")
312
301
col.set_resizable(False)
313
col.pack_start(cell, True)
302
col.pack_start(cell, expand=True)
314
303
col.add_attribute(cell, "markup", REVNO_COL)
315
304
tv.append_column(col)
317
cell = Gtk.CellRendererText()
306
cell = gtk.CellRendererText()
318
307
cell.set_property("ypad", 0)
319
308
cell.set_property("family", "Monospace")
320
col = Gtk.TreeViewColumn()
309
col = gtk.TreeViewColumn()
321
310
col.set_resizable(False)
322
col.pack_start(cell, True)
311
col.pack_start(cell, expand=True)
323
312
# col.add_attribute(cell, "foreground", HIGHLIGHT_COLOR_COL)
324
313
col.add_attribute(cell, "background", HIGHLIGHT_COLOR_COL)
325
314
col.add_attribute(cell, "text", TEXT_LINE_COL)
326
315
tv.append_column(col)
328
# interactive substring search
329
def search_equal_func(model, column, key, iter):
330
return model.get_value(iter, TEXT_LINE_COL).lower().find(key.lower()) == -1
332
tv.set_enable_search(True)
333
tv.set_search_equal_func(search_equal_func, None)
317
# FIXME: Now that C-f is now used for search by text we
318
# may as well disable the auto search.
319
tv.set_search_column(LINE_NUM_COL)
342
328
def _create_back_button(self):
343
button = Gtk.Button()
329
button = gtk.Button()
344
330
button.set_use_stock(True)
345
331
button.set_label("gtk-go-back")
346
332
button.connect("clicked", lambda w: self.go_back())
347
button.set_relief(Gtk.ReliefStyle.NONE)
333
button.set_relief(gtk.RELIEF_NONE)
351
337
def _create_forward_button(self):
352
button = Gtk.Button()
338
button = gtk.Button()
353
339
button.set_use_stock(True)
354
340
button.set_label("gtk-go-forward")
355
341
button.connect("clicked", lambda w: self.go_forward())
356
button.set_relief(Gtk.ReliefStyle.NONE)
342
button.set_relief(gtk.RELIEF_NONE)
358
344
button.set_sensitive(False)
361
def _create_find_button(self):
362
button = Gtk.Button()
363
button.set_use_stock(True)
364
button.set_label("gtk-find")
365
button.set_tooltip_text("Search for text (Ctrl+F)")
366
button.connect("clicked", self._search_by_text)
367
button.set_relief(Gtk.ReliefStyle.NONE)
369
button.set_sensitive(True)
372
def _create_goto_button(self):
373
button = Gtk.Button()
374
button.set_label("Goto Line")
375
button.set_tooltip_text("Scroll to a line by entering its number (Ctrl+G)")
376
button.connect("clicked", self._search_by_line)
377
button.set_relief(Gtk.ReliefStyle.NONE)
379
button.set_sensitive(True)
382
347
def go_back(self):
383
348
last_tree = self.tree
384
349
rev_id = self._selected_revision()
462
424
self.__cache[revision_id] = revision
463
425
return self.__cache[revision_id]
466
class SearchBox(Gtk.HBox):
427
class SearchBox(gtk.HBox):
467
428
"""A button box for searching in text or lines of annotations"""
468
429
def __init__(self):
469
super(SearchBox, self).__init__(homogeneous=False, spacing=6)
430
gtk.HBox.__init__(self, False, 6)
472
button = Gtk.Button()
474
image.set_from_stock('gtk-stop', Gtk.IconSize.BUTTON)
433
button = gtk.Button()
435
image.set_from_stock('gtk-stop', gtk.ICON_SIZE_BUTTON)
475
436
button.set_image(image)
476
button.set_relief(Gtk.ReliefStyle.NONE)
477
button.connect("clicked", lambda w: self.hide())
478
self.pack_start(button, False, False, 0)
437
button.set_relief(gtk.RELIEF_NONE)
438
button.connect("clicked", lambda w: self.hide_all())
439
self.pack_start(button, expand=False, fill=False)
482
443
self._label = label
483
self.pack_start(label, False, False, 0)
444
self.pack_start(label, expand=False, fill=False)
486
447
self._entry = entry
487
448
entry.connect("activate", lambda w, d: self._do_search(d),
489
self.pack_start(entry, False, False, 0)
450
self.pack_start(entry, expand=False, fill=False)
491
452
# Next/previous buttons
492
button = Gtk.Button(_i18n('_Next'), use_underline=True)
494
image.set_from_stock('gtk-go-forward', Gtk.IconSize.BUTTON)
453
button = gtk.Button('_Next')
455
image.set_from_stock('gtk-go-forward', gtk.ICON_SIZE_BUTTON)
495
456
button.set_image(image)
496
457
button.connect("clicked", lambda w, d: self._do_search(d),
498
self.pack_start(button, False, False, 0)
459
self.pack_start(button, expand=False, fill=False)
500
button = Gtk.Button(_i18n('_Previous'), use_underline=True)
502
image.set_from_stock('gtk-go-back', Gtk.IconSize.BUTTON)
461
button = gtk.Button('_Previous')
463
image.set_from_stock('gtk-go-back', gtk.ICON_SIZE_BUTTON)
503
464
button.set_image(image)
504
465
button.connect("clicked", lambda w, d: self._do_search(d),
506
self.pack_start(button, False, False, 0)
467
self.pack_start(button, expand=False, fill=False)
509
check = Gtk.CheckButton('Match case')
470
check = gtk.CheckButton('Match case')
510
471
self._match_case = check
511
self.pack_start(check, False, False, 0)
472
self.pack_start(check, expand=False, fill=False)
513
check = Gtk.CheckButton('Regexp')
474
check = gtk.CheckButton('Regexp')
514
475
check.connect("toggled", lambda w: self._set_label())
515
476
self._regexp = check
516
self.pack_start(check, False, False, 0)
477
self.pack_start(check, expand=False, fill=False)
518
479
self._view = None
519
480
self._column = None
547
508
def _match(self, model, iterator, column):
548
509
matching_case = self._match_case.get_active()
549
cell_value, = model.get(iterator, column)
510
string, = model.get(iterator, column)
550
511
key = self._entry.get_text()
551
if column == LINE_NUM_COL:
552
# FIXME: For goto-line there are faster algorithms than searching
553
# every line til we find the right one! -- mbp 2011-01-27
554
return key.strip() == str(cell_value)
555
elif self._regexp.get_active():
512
if self._regexp.get_active():
556
513
if matching_case:
557
match = re.compile(key).search(cell_value, 1)
514
match = re.compile(key).search(string, 1)
559
match = re.compile(key, re.I).search(cell_value, 1)
516
match = re.compile(key, re.I).search(string, 1)
561
518
if not matching_case:
562
cell_value = cell_value.lower()
519
string = string.lower()
563
520
key = key.lower()
564
match = cell_value.find(key) != -1
521
match = string.find(key) != -1