summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--grc/gui/BlockTreeWindow.py142
-rw-r--r--grc/gui/DrawingArea.py17
-rw-r--r--grc/gui/MainWindow.py2
3 files changed, 111 insertions, 50 deletions
diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py
index ced6429c62..013809cdea 100644
--- a/grc/gui/BlockTreeWindow.py
+++ b/grc/gui/BlockTreeWindow.py
@@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
"""
from Constants import DEFAULT_BLOCKS_WINDOW_WIDTH, DND_TARGETS
+import Actions
import Utils
import pygtk
pygtk.require('2.0')
@@ -46,7 +47,7 @@ class BlockTreeWindow(gtk.VBox):
Create a tree view of the possible blocks in the platform.
The tree view nodes will be category names, the leaves will be block names.
A mouse double click or button press action will trigger the add block event.
-
+
Args:
platform: the particular platform will all block prototypes
get_flow_graph: get the selected flow graph
@@ -54,21 +55,33 @@ class BlockTreeWindow(gtk.VBox):
gtk.VBox.__init__(self)
self.platform = platform
self.get_flow_graph = get_flow_graph
- #make the tree model for holding blocks
+
+ # search entry
+ self.search_entry = gtk.Entry()
+ self.search_entry.set_icon_from_stock(gtk.ENTRY_ICON_PRIMARY, gtk.STOCK_FIND)
+ self.search_entry.set_icon_activatable(gtk.ENTRY_ICON_PRIMARY, False)
+ self.search_entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, gtk.STOCK_CLOSE)
+ self.search_entry.connect('icon-release', self._handle_icon_event)
+ self.search_entry.connect('changed', self._update_search_tree)
+ self.search_entry.connect('key-press-event', self._handle_search_key_press)
+ self.pack_start(self.search_entry, False)
+
+ #make the tree model for holding blocks and a temporary one for search results
self.treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
+ self.treestore_search = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
+
self.treeview = gtk.TreeView(self.treestore)
self.treeview.set_enable_search(False) #disable pop up search box
+ self.treeview.set_search_column(-1) # really disable search
+ self.treeview.set_headers_visible(False)
self.treeview.add_events(gtk.gdk.BUTTON_PRESS_MASK)
self.treeview.connect('button-press-event', self._handle_mouse_button_press)
- selection = self.treeview.get_selection()
- selection.set_mode('single')
- selection.connect('changed', self._handle_selection_change)
+ self.treeview.connect('key-press-event', self._handle_search_key_press)
+
+ self.treeview.get_selection().set_mode('single')
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn('Blocks', renderer, text=NAME_INDEX)
self.treeview.append_column(column)
- #setup the search
- self.treeview.set_enable_search(True)
- self.treeview.set_search_equal_func(self._handle_search)
#try to enable the tooltips (available in pygtk 2.12 and above)
try: self.treeview.set_tooltip_column(DOC_INDEX)
except: pass
@@ -90,6 +103,7 @@ class BlockTreeWindow(gtk.VBox):
self.pack_start(self.add_button, False)
#map categories to iters, automatic mapping for root
self._categories = {tuple(): None}
+ self._categories_search = {tuple(): None}
#add blocks and categories
self.platform.load_block_tree(self)
#initialize
@@ -99,36 +113,38 @@ class BlockTreeWindow(gtk.VBox):
self.treestore.clear();
self._categories = {tuple(): None}
-
############################################################
## Block Tree Methods
############################################################
- def add_block(self, category, block=None):
+ def add_block(self, category, block=None, treestore=None, categories=None):
"""
Add a block with category to this selection window.
Add only the category when block is None.
-
+
Args:
category: the category list or path string
block: the block object or None
"""
+ if treestore is None: treestore = self.treestore
+ if categories is None: categories = self._categories
+
if isinstance(category, str): category = category.split('/')
category = tuple(filter(lambda x: x, category)) #tuple is hashable
#add category and all sub categories
for i, cat_name in enumerate(category):
sub_category = category[:i+1]
- if sub_category not in self._categories:
- iter = self.treestore.insert_before(self._categories[sub_category[:-1]], None)
- self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name)
- self.treestore.set_value(iter, KEY_INDEX, '')
- self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name))
- self._categories[sub_category] = iter
+ if sub_category not in categories:
+ iter = treestore.insert_before(categories[sub_category[:-1]], None)
+ treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name)
+ treestore.set_value(iter, KEY_INDEX, '')
+ treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name))
+ categories[sub_category] = iter
#add block
if block is None: return
- iter = self.treestore.insert_before(self._categories[category], None)
- self.treestore.set_value(iter, NAME_INDEX, block.get_name())
- self.treestore.set_value(iter, KEY_INDEX, block.get_key())
- self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc()))
+ iter = treestore.insert_before(categories[category], None)
+ treestore.set_value(iter, NAME_INDEX, block.get_name())
+ treestore.set_value(iter, KEY_INDEX, block.get_key())
+ treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc()))
############################################################
## Helper Methods
@@ -136,7 +152,7 @@ class BlockTreeWindow(gtk.VBox):
def _get_selected_block_key(self):
"""
Get the currently selected block key.
-
+
Returns:
the key of the selected block or a empty string
"""
@@ -157,26 +173,69 @@ class BlockTreeWindow(gtk.VBox):
Add the selected block with the given key to the flow graph.
"""
key = self._get_selected_block_key()
- if key: self.get_flow_graph().add_new_block(key)
+ if key:
+ self.get_flow_graph().add_new_block(key)
+ return True
+ return False
+
+ def _expand_category(self):
+ treestore, iter = self.treeview.get_selection().get_selected()
+ if iter and treestore.iter_has_child(iter):
+ path = self.treestore.get_path(iter)
+ self.treeview.expand_to_path(path)
############################################################
## Event Handlers
############################################################
- def _handle_search(self, model, column, key, iter):
- #determine which blocks match the search key
- blocks = self.get_flow_graph().get_parent().get_blocks()
- matching_blocks = filter(lambda b: key in b.get_key() or key in b.get_name().lower(), blocks)
- #remove the old search category
- try: self.treestore.remove(self._categories.pop((self._search_category, )))
- except (KeyError, AttributeError): pass #nothing to remove
- #create a search category
- if not matching_blocks: return
- self._search_category = 'Search: %s'%key
- for block in matching_blocks: self.add_block(self._search_category, block)
- #expand the search category
- path = self.treestore.get_path(self._categories[(self._search_category, )])
- self.treeview.collapse_all()
- self.treeview.expand_row(path, open_all=False)
+ def _handle_icon_event(self, widget, icon, event):
+ if icon == gtk.ENTRY_ICON_PRIMARY:
+ pass
+ elif icon == gtk.ENTRY_ICON_SECONDARY:
+ widget.set_text('')
+
+ def _update_search_tree(self, widget):
+ key = widget.get_text().lower()
+ if not key:
+ self.treeview.set_model(self.treestore)
+ self.treeview.collapse_all()
+ else:
+ blocks = self.get_flow_graph().get_parent().get_blocks()
+ matching_blocks = filter(lambda b: key in b.get_key().lower() or key in b.get_name().lower(), blocks)
+
+ self.treestore_search.clear()
+ self._categories_search = {tuple(): None}
+ for block in matching_blocks:
+ self.add_block(block.get_category() or 'None', block,
+ self.treestore_search, self._categories_search)
+ self.treeview.set_model(self.treestore_search)
+ self.treeview.expand_all()
+
+ def _handle_search_key_press(self, widget, event):
+ """Handle Return and Escape key events in search entry and treeview"""
+ if event.keyval == gtk.keysyms.Return:
+ # add block on enter
+
+ if widget == self.search_entry:
+ # Get the first block in the search tree and add it
+ selected = self.treestore_search.get_iter_first()
+ while self.treestore_search.iter_children(selected):
+ selected = self.treestore_search.iter_children(selected)
+ if selected is not None:
+ key = self.treestore_search.get_value(selected, KEY_INDEX)
+ if key: self.get_flow_graph().add_new_block(key)
+ elif widget == self.treeview:
+ self._add_selected_block() or self._expand_category()
+ else:
+ return False # propagate event
+
+ elif event.keyval == gtk.keysyms.Escape:
+ # reset the search
+ self.search_entry.set_text('')
+
+ else:
+ return False # propagate event
+
+ return True
def _handle_drag_get_data(self, widget, drag_context, selection_data, info, time):
"""
@@ -195,13 +254,6 @@ class BlockTreeWindow(gtk.VBox):
if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
self._add_selected_block()
- def _handle_selection_change(self, selection):
- """
- Handle a selection change in the tree view.
- If a selection changes, set the add button sensitive.
- """
- self._update_add_button()
-
def _handle_add_button(self, widget):
"""
Handle the add button clicked signal.
diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py
index 64be4d3ea6..1313e90420 100644
--- a/grc/gui/DrawingArea.py
+++ b/grc/gui/DrawingArea.py
@@ -51,7 +51,8 @@ class DrawingArea(gtk.DrawingArea):
gtk.gdk.POINTER_MOTION_MASK | \
gtk.gdk.BUTTON_RELEASE_MASK | \
gtk.gdk.LEAVE_NOTIFY_MASK | \
- gtk.gdk.ENTER_NOTIFY_MASK
+ gtk.gdk.ENTER_NOTIFY_MASK | \
+ gtk.gdk.FOCUS_CHANGE_MASK
)
#setup drag and drop
self.drag_dest_set(gtk.DEST_DEFAULT_ALL, DND_TARGETS, gtk.gdk.ACTION_COPY)
@@ -59,9 +60,11 @@ class DrawingArea(gtk.DrawingArea):
#setup the focus flag
self._focus_flag = False
self.get_focus_flag = lambda: self._focus_flag
- def _handle_focus_event(widget, event, focus_flag): self._focus_flag = focus_flag
- self.connect('leave-notify-event', _handle_focus_event, False)
- self.connect('enter-notify-event', _handle_focus_event, True)
+ def _handle_notify_event(widget, event, focus_flag): self._focus_flag = focus_flag
+ self.connect('leave-notify-event', _handle_notify_event, False)
+ self.connect('enter-notify-event', _handle_notify_event, True)
+ self.set_can_focus(True)
+ self.connect('focus-out-event', self._handle_focus_lost_event)
def new_pixmap(self, width, height): return gtk.gdk.Pixmap(self.window, width, height, -1)
def get_pixbuf(self):
@@ -83,6 +86,7 @@ class DrawingArea(gtk.DrawingArea):
"""
Forward button click information to the flow graph.
"""
+ self.grab_focus()
self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK
if event.button == 1: self._flow_graph.handle_mouse_selector_press(
double_click=(event.type == gtk.gdk._2BUTTON_PRESS),
@@ -133,3 +137,8 @@ class DrawingArea(gtk.DrawingArea):
gc = self.window.new_gc()
self._flow_graph.draw(gc, self._pixmap)
self.window.draw_drawable(gc, self._pixmap, 0, 0, 0, 0, -1, -1)
+
+ def _handle_focus_lost_event(self, widget, event):
+ self._flow_graph.unselect()
+ self._flow_graph.update_selected()
+ self._flow_graph.queue_draw() \ No newline at end of file
diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py
index 677f202e1f..dd378ad079 100644
--- a/grc/gui/MainWindow.py
+++ b/grc/gui/MainWindow.py
@@ -304,7 +304,7 @@ class MainWindow(gtk.Window):
def get_focus_flag(self):
"""
Get the focus flag from the current page.
-
+
Returns:
the focus flag
"""