diff options
Diffstat (limited to 'grc')
-rw-r--r-- | grc/gui/BlockTreeWindow.py | 142 | ||||
-rw-r--r-- | grc/gui/DrawingArea.py | 17 | ||||
-rw-r--r-- | grc/gui/MainWindow.py | 2 |
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 """ |