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