* 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) |