Imported Upstream version 3.2.2
[debian/gnuradio] / grc / gui / BlockTreeWindow.py
1 """
2 Copyright 2007, 2008, 2009 Free Software Foundation, Inc.
3 This file is part of GNU Radio
4
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.
9
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.
14
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
18 """
19
20 from Constants import DEFAULT_BLOCKS_WINDOW_WIDTH, DND_TARGETS
21 import Utils
22 import pygtk
23 pygtk.require('2.0')
24 import gtk
25 import gobject
26
27 NAME_INDEX = 0
28 KEY_INDEX = 1
29 DOC_INDEX = 2
30
31 class BlockTreeWindow(gtk.VBox):
32         """The block selection panel."""
33
34         def __init__(self, platform, get_flow_graph):
35                 """
36                 BlockTreeWindow constructor.
37                 Create a tree view of the possible blocks in the platform.
38                 The tree view nodes will be category names, the leaves will be block names.
39                 A mouse double click or button press action will trigger the add block event.
40                 @param platform the particular platform will all block prototypes
41                 @param get_flow_graph get the selected flow graph
42                 """
43                 gtk.VBox.__init__(self)
44                 self.platform = platform
45                 self.get_flow_graph = get_flow_graph
46                 #make the tree model for holding blocks
47                 self.treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
48                 self.treeview = gtk.TreeView(self.treestore)
49                 self.treeview.set_enable_search(False) #disable pop up search box
50                 self.treeview.add_events(gtk.gdk.BUTTON_PRESS_MASK)
51                 self.treeview.connect('button_press_event', self._handle_mouse_button_press)
52                 selection = self.treeview.get_selection()
53                 selection.set_mode('single')
54                 selection.connect('changed', self._handle_selection_change)
55                 renderer = gtk.CellRendererText()
56                 column = gtk.TreeViewColumn('Blocks', renderer, text=NAME_INDEX)
57                 self.treeview.append_column(column)
58                 #try to enable the tooltips (available in pygtk 2.12 and above) 
59                 try: self.treeview.set_tooltip_column(DOC_INDEX)
60                 except: pass
61                 #setup drag and drop
62                 self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, DND_TARGETS, gtk.gdk.ACTION_COPY)
63                 self.treeview.connect('drag-data-get', self._handle_drag_get_data)
64                 #make the scrolled window to hold the tree view
65                 scrolled_window = gtk.ScrolledWindow()
66                 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
67                 scrolled_window.add_with_viewport(self.treeview)
68                 scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
69                 self.pack_start(scrolled_window)
70                 #add button
71                 self.add_button = gtk.Button(None, gtk.STOCK_ADD)
72                 self.add_button.connect('clicked', self._handle_add_button)
73                 self.pack_start(self.add_button, False)
74                 #map categories to iters, automatic mapping for root
75                 self._categories = {tuple(): None}
76                 #add blocks and categories
77                 self.platform.load_block_tree(self)
78                 #initialize
79                 self._update_add_button()
80
81         ############################################################
82         ## Block Tree Methods
83         ############################################################
84         def add_block(self, category, block=None):
85                 """
86                 Add a block with category to this selection window.
87                 Add only the category when block is None.
88                 @param category the category list or path string
89                 @param block the block object or None
90                 """
91                 if isinstance(category, str): category = category.split('/')
92                 category = tuple(filter(lambda x: x, category)) #tuple is hashable
93                 #add category and all sub categories
94                 for i, cat_name in enumerate(category):
95                         sub_category = category[:i+1]
96                         if sub_category not in self._categories:
97                                 iter = self.treestore.insert_before(self._categories[sub_category[:-1]], None)
98                                 self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name)
99                                 self.treestore.set_value(iter, KEY_INDEX, '')
100                                 self.treestore.set_value(iter, DOC_INDEX, Utils.xml_encode('Category: %s'%cat_name))
101                                 self._categories[sub_category] = iter
102                 #add block
103                 if block is None: return
104                 iter = self.treestore.insert_before(self._categories[category], None)
105                 self.treestore.set_value(iter, NAME_INDEX, block.get_name())
106                 self.treestore.set_value(iter, KEY_INDEX, block.get_key())
107                 self.treestore.set_value(iter, DOC_INDEX, Utils.xml_encode(block.get_doc() or 'undocumented'))
108
109         ############################################################
110         ## Helper Methods
111         ############################################################
112         def _get_selected_block_key(self):
113                 """
114                 Get the currently selected block key.
115                 @return the key of the selected block or a empty string
116                 """
117                 selection = self.treeview.get_selection()
118                 treestore, iter = selection.get_selected()
119                 return iter and treestore.get_value(iter, KEY_INDEX) or ''
120
121         def _update_add_button(self):
122                 """
123                 Update the add button's sensitivity.
124                 The button should be active only if a block is selected.
125                 """
126                 key = self._get_selected_block_key()
127                 self.add_button.set_sensitive(bool(key))
128
129         def _add_selected_block(self):
130                 """
131                 Add the selected block with the given key to the flow graph.
132                 """
133                 key = self._get_selected_block_key()
134                 if key: self.get_flow_graph().add_new_block(key)
135
136         ############################################################
137         ## Event Handlers
138         ############################################################
139         def _handle_drag_get_data(self, widget, drag_context, selection_data, info, time):
140                 """
141                 Handle a drag and drop by setting the key to the selection object.
142                 This will call the destination handler for drag and drop.
143                 Only call set when the key is valid to ignore DND from categories.
144                 """
145                 key = self._get_selected_block_key()
146                 if key: selection_data.set(selection_data.target, 8, key)
147
148         def _handle_mouse_button_press(self, widget, event):
149                 """
150                 Handle the mouse button press.
151                 If a left double click is detected, call add selected block.
152                 """
153                 if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
154                         self._add_selected_block()
155
156         def _handle_selection_change(self, selection):
157                 """
158                 Handle a selection change in the tree view.
159                 If a selection changes, set the add button sensitive.
160                 """
161                 self._update_add_button()
162
163         def _handle_add_button(self, widget):
164                 """
165                 Handle the add button clicked signal.
166                 Call add selected block.
167                 """
168                 self._add_selected_block()