Created skeleton wxgui term window component
[debian/gnuradio] / grc / gui / Element.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 import pygtk
21 pygtk.require('2.0')
22 import gtk
23 from Constants import LINE_SELECT_SENSITIVITY
24 from Constants import POSSIBLE_ROTATIONS
25
26 class Element(object):
27         """
28         GraphicalElement is the base class for all graphical elements.
29         It contains an X,Y coordinate, a list of rectangular areas that the element occupies,
30         and methods to detect selection of those areas.
31         """
32
33         def __init__(self):
34                 """
35                 Make a new list of rectangular areas and lines, and set the coordinate and the rotation.
36                 """
37                 self.set_rotation(POSSIBLE_ROTATIONS[0])
38                 self.set_coordinate((0, 0))
39                 self.clear()
40                 self.set_highlighted(False)
41
42         def is_horizontal(self, rotation=None):
43                 """
44                 Is this element horizontal?
45                 If rotation is None, use this element's rotation.
46                 @param rotation the optional rotation
47                 @return true if rotation is horizontal
48                 """
49                 rotation = rotation or self.get_rotation()
50                 return rotation in (0, 180)
51
52         def is_vertical(self, rotation=None):
53                 """
54                 Is this element vertical?
55                 If rotation is None, use this element's rotation.
56                 @param rotation the optional rotation
57                 @return true if rotation is vertical
58                 """
59                 rotation = rotation or self.get_rotation()
60                 return rotation in (90, 270)
61
62         def create_labels(self):
63                 """
64                 Create labels (if applicable) and call on all children.
65                 Call this base method before creating labels in the element.
66                 """
67                 for child in self.get_children(): child.create_labels()
68
69         def create_shapes(self):
70                 """
71                 Create shapes (if applicable) and call on all children.
72                 Call this base method before creating shapes in the element.
73                 """
74                 self.clear()
75                 for child in self.get_children(): child.create_shapes()
76
77         def draw(self, gc, window, border_color, bg_color):
78                 """
79                 Draw in the given window.
80                 @param gc the graphics context
81                 @param window the gtk window to draw on
82                 @param border_color the color for lines and rectangle borders
83                 @param bg_color the color for the inside of the rectangle
84                 """
85                 X,Y = self.get_coordinate()
86                 for (rX,rY),(W,H) in self._areas_list:
87                         aX = X + rX
88                         aY = Y + rY
89                         gc.set_foreground(bg_color)
90                         window.draw_rectangle(gc, True, aX, aY, W, H)
91                         gc.set_foreground(border_color)
92                         window.draw_rectangle(gc, False, aX, aY, W, H)
93                 for (x1, y1),(x2, y2) in self._lines_list:
94                         gc.set_foreground(border_color)
95                         window.draw_line(gc, X+x1, Y+y1, X+x2, Y+y2)
96
97         def rotate(self, rotation):
98                 """
99                 Rotate all of the areas by 90 degrees.
100                 @param rotation multiple of 90 degrees
101                 """
102                 self.set_rotation((self.get_rotation() + rotation)%360)
103
104         def clear(self):
105                 """Empty the lines and areas."""
106                 self._areas_list = list()
107                 self._lines_list = list()
108
109         def set_coordinate(self, coor):
110                 """
111                 Set the reference coordinate.
112                 @param coor the coordinate tuple (x,y)
113                 """
114                 self.coor = coor
115
116         def get_parent(self):
117                 """
118                 Get the parent of this element.
119                 @return the parent
120                 """
121                 return self.parent
122
123         def set_highlighted(self, highlighted):
124                 """
125                 Set the highlight status.
126                 @param highlighted true to enable highlighting
127                 """
128                 self.highlighted = highlighted
129
130         def is_highlighted(self):
131                 """
132                 Get the highlight status.
133                 @return true if highlighted
134                 """
135                 return self.highlighted
136
137         def get_coordinate(self):
138                 """Get the coordinate.
139                 @return the coordinate tuple (x,y)
140                 """
141                 return self.coor
142
143         def move(self, delta_coor):
144                 """
145                 Move the element by adding the delta_coor to the current coordinate.
146                 @param delta_coor (delta_x,delta_y) tuple
147                 """
148                 deltaX, deltaY = delta_coor
149                 X, Y = self.get_coordinate()
150                 self.set_coordinate((X+deltaX, Y+deltaY))
151
152         def add_area(self, rel_coor, area):
153                 """
154                 Add an area to the area list.
155                 An area is actually a coordinate relative to the main coordinate
156                 with a width/height pair relative to the area coordinate.
157                 A positive width is to the right of the coordinate.
158                 A positive height is above the coordinate.
159                 The area is associated with a rotation.
160                 @param rel_coor (x,y) offset from this element's coordinate
161                 @param area (width,height) tuple
162                 """
163                 self._areas_list.append((rel_coor, area))
164
165         def add_line(self, rel_coor1, rel_coor2):
166                 """
167                 Add a line to the line list.
168                 A line is defined by 2 relative coordinates.
169                 Lines must be horizontal or vertical.
170                 The line is associated with a rotation.
171                 @param rel_coor1 relative (x1,y1) tuple
172                 @param rel_coor2 relative (x2,y2) tuple
173                 """
174                 self._lines_list.append((rel_coor1, rel_coor2))
175
176         def what_is_selected(self, coor, coor_m=None):
177                 """
178                 One coordinate specified:
179                         Is this element selected at given coordinate?
180                         ie: is the coordinate encompassed by one of the areas or lines?
181                 Both coordinates specified:
182                         Is this element within the rectangular region defined by both coordinates?
183                         ie: do any area corners or line endpoints fall within the region?
184                 @param coor the selection coordinate, tuple x, y
185                 @param coor_m an additional selection coordinate.
186                 @return self if one of the areas/lines encompasses coor, else None.
187                 """
188                 #function to test if p is between a and b (inclusive)
189                 in_between = lambda p, a, b: p >= min(a, b) and p <= max(a, b)
190                 #relative coordinate
191                 x, y = [a-b for a,b in zip(coor, self.get_coordinate())]
192                 if coor_m:
193                         x_m, y_m = [a-b for a,b in zip(coor_m, self.get_coordinate())]
194                         #handle rectangular areas
195                         for (x1,y1), (w,h) in self._areas_list:
196                                 if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \
197                                         in_between(x1+w, x, x_m) and in_between(y1, y, y_m) or \
198                                         in_between(x1, x, x_m) and in_between(y1+h, y, y_m) or \
199                                         in_between(x1+w, x, x_m) and in_between(y1+h, y, y_m):
200                                         return self
201                         #handle horizontal or vertical lines
202                         for (x1, y1), (x2, y2) in self._lines_list:
203                                 if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \
204                                         in_between(x2, x, x_m) and in_between(y2, y, y_m):
205                                         return self
206                         return None
207                 else:
208                         #handle rectangular areas
209                         for (x1,y1), (w,h) in self._areas_list:
210                                 if in_between(x, x1, x1+w) and in_between(y, y1, y1+h): return self
211                         #handle horizontal or vertical lines
212                         for (x1, y1), (x2, y2) in self._lines_list:
213                                 if x1 == x2: x1, x2 = x1-LINE_SELECT_SENSITIVITY, x2+LINE_SELECT_SENSITIVITY
214                                 if y1 == y2: y1, y2 = y1-LINE_SELECT_SENSITIVITY, y2+LINE_SELECT_SENSITIVITY
215                                 if in_between(x, x1, x2) and in_between(y, y1, y2): return self
216                         return None
217
218         def get_rotation(self):
219                 """
220                 Get the rotation in degrees.
221                 @return the rotation
222                 """
223                 return self.rotation
224
225         def set_rotation(self, rotation):
226                 """
227                 Set the rotation in degrees.
228                 @param rotation the rotation"""
229                 if rotation not in POSSIBLE_ROTATIONS:
230                         raise Exception('"%s" is not one of the possible rotations: (%s)'%(rotation, POSSIBLE_ROTATIONS))
231                 self.rotation = rotation