Merge commit 'origin' into new_eth
[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 DOC_MARKUP_TMPL="""\
32 #if $doc
33 $encode($doc)#slurp
34 #else
35 undocumented#slurp
36 #end if"""
37
38 CAT_MARKUP_TMPL="""Category: $cat"""
39
40 class BlockTreeWindow(gtk.VBox):
41         """The block selection panel."""
42
43         def __init__(self, platform, get_flow_graph):
44                 """
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
51                 """
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)
67                 #try to enable the tooltips (available in pygtk 2.12 and above) 
68                 try: self.treeview.set_tooltip_column(DOC_INDEX)
69                 except: pass
70                 #setup drag and drop
71                 self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, DND_TARGETS, gtk.gdk.ACTION_COPY)
72                 self.treeview.connect('drag-data-get', self._handle_drag_get_data)
73                 #make the scrolled window to hold the tree view
74                 scrolled_window = gtk.ScrolledWindow()
75                 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
76                 scrolled_window.add_with_viewport(self.treeview)
77                 scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1)
78                 self.pack_start(scrolled_window)
79                 #add button
80                 self.add_button = gtk.Button(None, gtk.STOCK_ADD)
81                 self.add_button.connect('clicked', self._handle_add_button)
82                 self.pack_start(self.add_button, False)
83                 #map categories to iters, automatic mapping for root
84                 self._categories = {tuple(): None}
85                 #add blocks and categories
86                 self.platform.load_block_tree(self)
87                 #initialize
88                 self._update_add_button()
89
90         ############################################################
91         ## Block Tree Methods
92         ############################################################
93         def add_block(self, category, block=None):
94                 """
95                 Add a block with category to this selection window.
96                 Add only the category when block is None.
97                 @param category the category list or path string
98                 @param block the block object or None
99                 """
100                 if isinstance(category, str): category = category.split('/')
101                 category = tuple(filter(lambda x: x, category)) #tuple is hashable
102                 #add category and all sub categories
103                 for i, cat_name in enumerate(category):
104                         sub_category = category[:i+1]
105                         if sub_category not in self._categories:
106                                 iter = self.treestore.insert_before(self._categories[sub_category[:-1]], None)
107                                 self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name)
108                                 self.treestore.set_value(iter, KEY_INDEX, '')
109                                 self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name))
110                                 self._categories[sub_category] = iter
111                 #add block
112                 if block is None: return
113                 iter = self.treestore.insert_before(self._categories[category], None)
114                 self.treestore.set_value(iter, NAME_INDEX, block.get_name())
115                 self.treestore.set_value(iter, KEY_INDEX, block.get_key())
116                 self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc()))
117
118         ############################################################
119         ## Helper Methods
120         ############################################################
121         def _get_selected_block_key(self):
122                 """
123                 Get the currently selected block key.
124                 @return the key of the selected block or a empty string
125                 """
126                 selection = self.treeview.get_selection()
127                 treestore, iter = selection.get_selected()
128                 return iter and treestore.get_value(iter, KEY_INDEX) or ''
129
130         def _update_add_button(self):
131                 """
132                 Update the add button's sensitivity.
133                 The button should be active only if a block is selected.
134                 """
135                 key = self._get_selected_block_key()
136                 self.add_button.set_sensitive(bool(key))
137
138         def _add_selected_block(self):
139                 """
140                 Add the selected block with the given key to the flow graph.
141                 """
142                 key = self._get_selected_block_key()
143                 if key: self.get_flow_graph().add_new_block(key)
144
145         ############################################################
146         ## Event Handlers
147         ############################################################
148         def _handle_drag_get_data(self, widget, drag_context, selection_data, info, time):
149                 """
150                 Handle a drag and drop by setting the key to the selection object.
151                 This will call the destination handler for drag and drop.
152                 Only call set when the key is valid to ignore DND from categories.
153                 """
154                 key = self._get_selected_block_key()
155                 if key: selection_data.set(selection_data.target, 8, key)
156
157         def _handle_mouse_button_press(self, widget, event):
158                 """
159                 Handle the mouse button press.
160                 If a left double click is detected, call add selected block.
161                 """
162                 if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS:
163                         self._add_selected_block()
164
165         def _handle_selection_change(self, selection):
166                 """
167                 Handle a selection change in the tree view.
168                 If a selection changes, set the add button sensitive.
169                 """
170                 self._update_add_button()
171
172         def _handle_add_button(self, widget):
173                 """
174                 Handle the add button clicked signal.
175                 Call add selected block.
176                 """
177                 self._add_selected_block()