* Always pan to the first result
* Show the number of results
* Provide a button to pan to the next result
... | ... |
@@ -92,6 +92,9 @@ class Shape: |
92 | 92 |
ya, yb = min(ya, y0), max(yb, y1) |
93 | 93 |
return xa, ya, xb, yb |
94 | 94 |
|
95 |
+ def get_text(self): |
|
96 |
+ return None |
|
97 |
+ |
|
95 | 98 |
|
96 | 99 |
class TextShape(Shape): |
97 | 100 |
|
... | ... |
@@ -211,6 +214,9 @@ class TextShape(Shape): |
211 | 214 |
x, w, j = self.x, self.w, self.j |
212 | 215 |
return x - 0.5 * (1 + j) * w, -_inf, x + 0.5 * (1 - j) * w, _inf |
213 | 216 |
|
217 |
+ def get_text(self): |
|
218 |
+ return self.t |
|
219 |
+ |
|
214 | 220 |
|
215 | 221 |
class ImageShape(Shape): |
216 | 222 |
|
... | ... |
@@ -494,6 +500,13 @@ class CompoundShape(Shape): |
494 | 500 |
return True |
495 | 501 |
return False |
496 | 502 |
|
503 |
+ def get_text(self): |
|
504 |
+ for shape in self.shapes: |
|
505 |
+ text = shape.get_text() |
|
506 |
+ if text is not None: |
|
507 |
+ return text |
|
508 |
+ return None |
|
509 |
+ |
|
497 | 510 |
|
498 | 511 |
class Url(object): |
499 | 512 |
|
... | ... |
@@ -20,6 +20,7 @@ import re |
20 | 20 |
import subprocess |
21 | 21 |
import sys |
22 | 22 |
import time |
23 |
+import operator |
|
23 | 24 |
|
24 | 25 |
import gi |
25 | 26 |
gi.require_version('Gtk', '3.0') |
... | ... |
@@ -534,6 +535,10 @@ class DotWindow(Gtk.Window): |
534 | 535 |
<toolitem action="Zoom100"/> |
535 | 536 |
<separator/> |
536 | 537 |
<toolitem name="Find" action="Find"/> |
538 |
+ <separator name="FindNextSeparator"/> |
|
539 |
+ <toolitem action="FindNext"/> |
|
540 |
+ <separator name="FindStatusSeparator"/> |
|
541 |
+ <toolitem name="FindStatus" action="FindStatus"/> |
|
537 | 542 |
</toolbar> |
538 | 543 |
</ui> |
539 | 544 |
''' |
... | ... |
@@ -577,6 +582,7 @@ class DotWindow(Gtk.Window): |
577 | 582 |
('ZoomOut', Gtk.STOCK_ZOOM_OUT, None, None, None, self.dotwidget.on_zoom_out), |
578 | 583 |
('ZoomFit', Gtk.STOCK_ZOOM_FIT, None, None, None, self.dotwidget.on_zoom_fit), |
579 | 584 |
('Zoom100', Gtk.STOCK_ZOOM_100, None, None, None, self.dotwidget.on_zoom_100), |
585 |
+ ('FindNext', Gtk.STOCK_GO_FORWARD, 'Next Result', None, 'Move to the next search result', self.on_find_next), |
|
580 | 586 |
)) |
581 | 587 |
|
582 | 588 |
self.back_action = Gtk.Action('Back', None, None, Gtk.STOCK_GO_BACK) |
... | ... |
@@ -593,6 +599,10 @@ class DotWindow(Gtk.Window): |
593 | 599 |
"Find a node by name", None) |
594 | 600 |
actiongroup.add_action(find_action) |
595 | 601 |
|
602 |
+ findstatus_action = FindMenuToolAction("FindStatus", None, |
|
603 |
+ "Number of results found", None) |
|
604 |
+ actiongroup.add_action(findstatus_action) |
|
605 |
+ |
|
596 | 606 |
# Add the actiongroup to the uimanager |
597 | 607 |
uimanager.insert_action_group(actiongroup, 0) |
598 | 608 |
|
... | ... |
@@ -619,6 +629,15 @@ class DotWindow(Gtk.Window): |
619 | 629 |
self.textentry.connect("activate", self.textentry_activate, self.textentry); |
620 | 630 |
self.textentry.connect("changed", self.textentry_changed, self.textentry); |
621 | 631 |
|
632 |
+ uimanager.get_widget('/ToolBar/FindNextSeparator').set_draw(False) |
|
633 |
+ uimanager.get_widget('/ToolBar/FindStatusSeparator').set_draw(False) |
|
634 |
+ self.find_next_toolitem = uimanager.get_widget('/ToolBar/FindNext') |
|
635 |
+ self.find_next_toolitem.set_sensitive(False) |
|
636 |
+ |
|
637 |
+ self.find_count = Gtk.Label() |
|
638 |
+ findstatus_toolitem = uimanager.get_widget('/ToolBar/FindStatus') |
|
639 |
+ findstatus_toolitem.add(self.find_count) |
|
640 |
+ |
|
622 | 641 |
self.show_all() |
623 | 642 |
|
624 | 643 |
def find_text(self, entry_text): |
... | ... |
@@ -628,9 +647,11 @@ class DotWindow(Gtk.Window): |
628 | 647 |
for element in dot_widget.graph.nodes + dot_widget.graph.edges: |
629 | 648 |
if element.search_text(regexp): |
630 | 649 |
found_items.append(element) |
631 |
- return found_items |
|
650 |
+ return sorted(found_items, key=operator.methodcaller('get_text')) |
|
632 | 651 |
|
633 | 652 |
def textentry_changed(self, widget, entry): |
653 |
+ self.find_index = 0 |
|
654 |
+ self.find_next_toolitem.set_sensitive(False) |
|
634 | 655 |
entry_text = entry.get_text() |
635 | 656 |
dot_widget = self.dotwidget |
636 | 657 |
if not entry_text: |
... | ... |
@@ -639,8 +660,14 @@ class DotWindow(Gtk.Window): |
639 | 660 |
|
640 | 661 |
found_items = self.find_text(entry_text) |
641 | 662 |
dot_widget.set_highlight(found_items, search=True) |
663 |
+ if found_items: |
|
664 |
+ self.find_count.set_label('%d nodes found' % len(found_items)) |
|
665 |
+ else: |
|
666 |
+ self.find_count.set_label('') |
|
642 | 667 |
|
643 | 668 |
def textentry_activate(self, widget, entry): |
669 |
+ self.find_index = 0 |
|
670 |
+ self.find_next_toolitem.set_sensitive(False) |
|
644 | 671 |
entry_text = entry.get_text() |
645 | 672 |
dot_widget = self.dotwidget |
646 | 673 |
if not entry_text: |
... | ... |
@@ -649,8 +676,9 @@ class DotWindow(Gtk.Window): |
649 | 676 |
|
650 | 677 |
found_items = self.find_text(entry_text) |
651 | 678 |
dot_widget.set_highlight(found_items, search=True) |
652 |
- if(len(found_items) == 1): |
|
679 |
+ if found_items: |
|
653 | 680 |
dot_widget.animate_to(found_items[0].x, found_items[0].y) |
681 |
+ self.find_next_toolitem.set_sensitive(len(found_items) > 1) |
|
654 | 682 |
|
655 | 683 |
def set_filter(self, filter): |
656 | 684 |
self.dotwidget.set_filter(filter) |
... | ... |
@@ -717,6 +745,15 @@ class DotWindow(Gtk.Window): |
717 | 745 |
dlg.run() |
718 | 746 |
dlg.destroy() |
719 | 747 |
|
748 |
+ def on_find_next(self, action): |
|
749 |
+ self.find_index += 1 |
|
750 |
+ entry_text = self.textentry.get_text() |
|
751 |
+ # Maybe storing the search result would be better |
|
752 |
+ found_items = self.find_text(entry_text) |
|
753 |
+ found_item = found_items[self.find_index] |
|
754 |
+ self.dotwidget.animate_to(found_item.x, found_item.y) |
|
755 |
+ self.find_next_toolitem.set_sensitive(len(found_items) > self.find_index + 1) |
|
756 |
+ |
|
720 | 757 |
def on_history(self, action, has_back, has_forward): |
721 | 758 |
self.back_action.set_sensitive(has_back) |
722 | 759 |
self.forward_action.set_sensitive(has_forward) |