Merged r9481:9518 on jblum/grc_reorganize into trunk. Reorganized grc source under...
[debian/gnuradio] / grc / src / platforms / gui / Block.py
1 """
2 Copyright 2007 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 ... gui import Preferences
21 from Element import Element
22 import Utils
23 import Colors
24 from ... gui.Constants import BORDER_PROXIMITY_SENSITIVITY
25 from Constants import \
26         BLOCK_FONT, LABEL_PADDING_WIDTH, \
27         LABEL_PADDING_HEIGHT, PORT_HEIGHT, \
28         PORT_SEPARATION, LABEL_SEPARATION, \
29         PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS
30 import pygtk
31 pygtk.require('2.0')
32 import gtk
33 import pango
34
35 class Block(Element):
36         """The graphical signal block."""
37
38         def __init__(self, *args, **kwargs):
39                 """
40                 Block contructor.
41                 Add graphics related params to the block.
42                 """
43                 #add the position param
44                 self._params['_coordinate'] = self.get_parent().get_parent().Param(
45                         self,
46                         {
47                                 'name': 'GUI Coordinate',
48                                 'key': '_coordinate',
49                                 'type': 'raw',
50                                 'value': '(0, 0)',
51                                 'hide': 'all',
52                         }
53                 )
54                 self._params['_rotation'] = self.get_parent().get_parent().Param(
55                         self,
56                         {
57                                 'name': 'GUI Rotation',
58                                 'key': '_rotation',
59                                 'type': 'raw',
60                                 'value': '0',
61                                 'hide': 'all',
62                         }
63                 )
64                 Element.__init__(self)
65
66         def get_coordinate(self):
67                 """
68                 Get the coordinate from the position param.
69                 @return the coordinate tuple (x, y) or (0, 0) if failure
70                 """
71                 try: #should evaluate to tuple
72                         coor = eval(self.get_param('_coordinate').get_value())
73                         x, y = map(int, coor)
74                         fgW,fgH = self.get_parent().get_size()
75                         if x <= 0:
76                                 x = 0
77                         elif x >= fgW - BORDER_PROXIMITY_SENSITIVITY:
78                                 x = fgW - BORDER_PROXIMITY_SENSITIVITY
79                         if y <= 0:
80                                 y = 0
81                         elif y >= fgH - BORDER_PROXIMITY_SENSITIVITY:
82                                 y = fgH - BORDER_PROXIMITY_SENSITIVITY
83                         return (x, y)
84                 except:
85                         self.set_coordinate((0, 0))
86                         return (0, 0)
87
88         def set_coordinate(self, coor):
89                 """
90                 Set the coordinate into the position param.
91                 @param coor the coordinate tuple (x, y)
92                 """
93                 self.get_param('_coordinate').set_value(str(coor))
94
95         def get_rotation(self):
96                 """
97                 Get the rotation from the position param.
98                 @return the rotation in degrees or 0 if failure
99                 """
100                 try: #should evaluate to dict
101                         rotation = eval(self.get_param('_rotation').get_value())
102                         return int(rotation)
103                 except:
104                         self.set_rotation(POSSIBLE_ROTATIONS[0])
105                         return POSSIBLE_ROTATIONS[0]
106
107         def set_rotation(self, rot):
108                 """
109                 Set the rotation into the position param.
110                 @param rot the rotation in degrees
111                 """
112                 self.get_param('_rotation').set_value(str(rot))
113
114         def update(self):
115                 """Update the block, parameters, and ports when a change occurs."""
116                 self.bg_color = self.get_enabled() and Colors.BG_COLOR or Colors.DISABLED_BG_COLOR
117                 self.clear()
118                 self._create_labels()
119                 self.W = self.label_width + 2*LABEL_PADDING_WIDTH
120                 max_ports = max(len(self.get_sinks()), len(self.get_sources()), 1)
121                 self.H = max(self.label_height+2*LABEL_PADDING_HEIGHT, 2*PORT_BORDER_SEPARATION + max_ports*PORT_HEIGHT + (max_ports-1)*PORT_SEPARATION)
122                 if self.is_horizontal(): self.add_area((0,0),(self.W,self.H))
123                 elif self.is_vertical(): self.add_area((0,0),(self.H,self.W))
124                 map(lambda p: p.update(), self.get_sinks() + self.get_sources())
125
126         def _create_labels(self):
127                 """Create the labels for the signal block."""
128                 layouts = list()
129                 #create the main layout
130                 layout = gtk.DrawingArea().create_pango_layout('')
131                 layouts.append(layout)
132                 if self.is_valid():     layout.set_markup('<b>'+Utils.xml_encode(self.get_name())+'</b>')
133                 else: layout.set_markup('<span foreground="red"><b>'+Utils.xml_encode(self.get_name())+'</b></span>')
134                 desc = pango.FontDescription(BLOCK_FONT)
135                 layout.set_font_description(desc)
136                 self.label_width, self.label_height = layout.get_pixel_size()
137                 #display the params (except for the special params id and position)
138                 if Preferences.show_params():
139                         for param in filter(lambda p: p.get_hide() not in ('all', 'part'), self.get_params()):
140                                 if not Preferences.show_id() and param.get_key() == 'id': continue
141                                 layout = param.get_layout()
142                                 layouts.append(layout)
143                                 w,h = layout.get_pixel_size()
144                                 self.label_width = max(w, self.label_width)
145                                 self.label_height = self.label_height + h + LABEL_SEPARATION
146                 width = self.label_width
147                 height = self.label_height
148                 #setup the pixmap
149                 pixmap = gtk.gdk.Pixmap(self.get_parent().get_window(), width, height, -1)
150                 gc = pixmap.new_gc()
151                 gc.foreground = self.bg_color
152                 pixmap.draw_rectangle(gc, True, 0, 0, width, height)
153                 gc.foreground = Colors.TXT_COLOR
154                 #draw the layouts
155                 h_off = 0
156                 for i,layout in enumerate(layouts):
157                         w,h = layout.get_pixel_size()
158                         if i == 0: w_off = (width-w)/2
159                         else: w_off = 0
160                         pixmap.draw_layout(gc, w_off, h_off, layout)
161                         h_off = h + h_off + LABEL_SEPARATION
162                 #create vertical and horizontal images
163                 self.horizontal_label = image = pixmap.get_image(0, 0, width, height)
164                 if self.is_vertical():
165                         self.vertical_label = vimage = gtk.gdk.Image(gtk.gdk.IMAGE_NORMAL, pixmap.get_visual(), height, width)
166                         for i in range(width):
167                                 for j in range(height): vimage.put_pixel(j, width-i-1, image.get_pixel(i, j))
168
169         def draw(self, window):
170                 """
171                 Draw the signal block with label and inputs/outputs.
172                 @param window the gtk window to draw on
173                 """
174                 x, y = self.get_coordinate()
175                 #draw main block
176                 Element.draw(self, window, BG_color=self.bg_color)
177                 #draw label image
178                 gc = self.get_gc()
179                 if self.is_horizontal():
180                         window.draw_image(gc, self.horizontal_label, 0, 0, x+LABEL_PADDING_WIDTH, y+(self.H-self.label_height)/2, -1, -1)
181                 elif self.is_vertical():
182                         window.draw_image(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+LABEL_PADDING_WIDTH, -1, -1)
183                 #draw ports
184                 map(lambda p: p.draw(window), self.get_ports())
185
186         def what_is_selected(self, coor, coor_m=None):
187                 """
188                 Get the element that is selected.
189                 @param coor the (x,y) tuple
190                 @param coor_m the (x_m, y_m) tuple
191                 @return this block, a port, or None
192                 """
193                 for port in self.get_ports():
194                         port_selected = port.what_is_selected(coor, coor_m)
195                         if port_selected: return port_selected
196                 return Element.what_is_selected(self, coor, coor_m)