2 Copyright 2007, 2008, 2009 Free Software Foundation, Inc.
3 This file is part of GNU Radio
5 GNU Radio Companion is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 GNU Radio Companion is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
20 from Constants import DEFAULT_BLOCKS_WINDOW_WIDTH, DND_TARGETS
38 CAT_MARKUP_TMPL="""Category: $cat"""
40 class BlockTreeWindow(gtk.VBox):
41 """The block selection panel."""
43 def __init__(self, platform, get_flow_graph):
45 BlockTreeWindow constructor.
46 Create a tree view of the possible blocks in the platform.
47 The tree view nodes will be category names, the leaves will be block names.
48 A mouse double click or button press action will trigger the add block event.
49 @param platform the particular platform will all block prototypes
50 @param get_flow_graph get the selected flow graph
52 gtk.VBox.__init__(self)
53 self.platform = platform
54 self.get_flow_graph = get_flow_graph
55 #make the tree model for holding blocks
56 self.treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
57 self.treeview = gtk.TreeView(self.treestore)
58 self.treeview.set_enable_search(False) #disable pop up search box
59 self.treeview.add_events(gtk.gdk.BUTTON_PRESS_MASK)
60 self.treeview.connect('button-press-event', self._handle_mouse_button_press)
61 selection = self.treeview.get_selection()
62 selection.set_mode('single')
63 selection.connect('changed', self._handle_selection_change)
64 renderer = gtk.CellRendererText()
65 column = gtk.TreeViewColumn('Blocks', renderer, text=NAME_INDEX)
66 self.treeview.append_column(column)
68 self.treeview.set_enable_search(True)
69 self.treeview.set_search_equal_func(self._handle_search)
70 #try to enable the tooltips (available in pygtk 2.12 and above)
71 try: self.treeview.set_tooltip_column(DOC_INDEX)
74 self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, DND_TARGETS, gtk.gdk.ACTION_COPY)
75 self.treeview.connect('drag-data-get', self._handle_drag_get_data)
76 #make the scrolled window to hold the tree view
77 scrolled_window = gtk.ScrolledWindow()
78 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
79 scrolled_window.add_with_viewport(self.treeview)
80 scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
81 self.pack_start(scrolled_window)
83 self.add_button = gtk.Button(None, gtk.STOCK_ADD)
84 self.add_button.connect('clicked', self._handle_add_button)
85 self.pack_start(self.add_button, False)
86 #map categories to iters, automatic mapping for root
87 self._categories = {tuple(): None}
88 #add blocks and categories
89 self.platform.load_block_tree(self)
91 self._update_add_button()
93 ############################################################
95 ############################################################
96 def add_block(self, category, block=None):
98 Add a block with category to this selection window.
99 Add only the category when block is None.
100 @param category the category list or path string
101 @param block the block object or None
103 if isinstance(category, str): category = category.split('/')
104 category = tuple(filter(lambda x: x, category)) #tuple is hashable
105 #add category and all sub categories
106 for i, cat_name in enumerate(category):
107 sub_category = category[:i+1]
108 if sub_category not in self._categories:
109 iter = self.treestore.insert_before(self._categories[sub_category[:-1]], None)
110 self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name)
111 self.treestore.set_value(iter, KEY_INDEX, '')
112 self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name))
113 self._categories[sub_category] = iter
115 if block is None: return
116 iter = self.treestore.insert_before(self._categories[category], None)
117 self.treestore.set_value(iter, NAME_INDEX, block.get_name())
118 self.treestore.set_value(iter, KEY_INDEX, block.get_key())
119 self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc()))
121 ############################################################
123 ############################################################
124 def _get_selected_block_key(self):
126 Get the currently selected block key.
127 @return the key of the selected block or a empty string
129 selection = self.treeview.get_selection()
130 treestore, iter = selection.get_selected()
131 return iter and treestore.get_value(iter, KEY_INDEX) or ''
133 def _update_add_button(self):
135 Update the add button's sensitivity.
136 The button should be active only if a block is selected.
138 key = self._get_selected_block_key()
139 self.add_button.set_sensitive(bool(key))
141 def _add_selected_block(self):
143 Add the selected block with the given key to the flow graph.
145 key = self._get_selected_block_key()
146 if key: self.get_flow_graph().add_new_block(key)
148 ############################################################
150 ############################################################
151 def _handle_search(self, model, column, key, iter):
152 #determine which blocks match the search key
153 blocks = self.get_flow_graph().get_parent().get_blocks()
154 matching_blocks = filter(lambda b: key in b.get_key() or key in b.get_name().lower(), blocks)
155 #remove the old search category
156 try: self.treestore.remove(self._categories.pop((self._search_category, )))
157 except (KeyError, AttributeError): pass #nothing to remove
158 #create a search category
159 if not matching_blocks: return
160 self._search_category = 'Search: %s'%key
161 for block in matching_blocks: self.add_block(self._search_category, block)
162 #expand the search category
163 path = self.treestore.get_path(self._categories[(self._search_category, )])
164 self.treeview.collapse_all()
165 self.treeview.expand_row(path, open_all=False)
167 def _handle_drag_get_data(self, widget, drag_context, selection_data, info, time):
169 Handle a drag and drop by setting the key to the selection object.
170 This will call the destination handler for drag and drop.
171 Only call set when the key is valid to ignore DND from categories.
173 key = self._get_selected_block_key()
174 if key: selection_data.set(selection_data.target, 8, key)
176 def _handle_mouse_button_press(self, widget, event):
178 Handle the mouse button press.
179 If a left double click is detected, call add selected block.
181 if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
182 self._add_selected_block()
184 def _handle_selection_change(self, selection):
186 Handle a selection change in the tree view.
187 If a selection changes, set the add button sensitive.
189 self._update_add_button()
191 def _handle_add_button(self, widget):
193 Handle the add button clicked signal.
194 Call add selected block.
196 self._add_selected_block()