c2e5edcfdf596ce1c337c03547275eb1a4825730
[debian/gnuradio] / grc / src / grc / gui / elements / Connection.py
1 """
2 Copyright 2007, 2008 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.Connection
20 #The graphical connection for input/output ports.
21 #@author Josh Blum
22
23 import Utils
24 from Element import Element
25 import Colors
26 from grc.Constants import CONNECTOR_ARROW_BASE,CONNECTOR_ARROW_HEIGHT
27
28 class Connection(Element):
29         """A graphical connection for ports."""
30
31         def get_coordinate(self):
32                 """!
33                 Get the 0,0 coordinate.
34                 Coordinates are irrelevant in connection.
35                 @return 0, 0
36                 """
37                 return (0, 0)
38
39         def get_rotation(self):
40                 """!
41                 Get the 0 degree rotation.
42                 Rotations are irrelevant in connection.
43                 @return 0
44                 """
45                 return 0
46
47         def update(self):
48                 """Precalculate relative coordinates."""
49                 self._sink_rot = None
50                 self._source_rot = None
51                 self._sink_coor = None
52                 self._source_coor = None
53
54                 sink = self.get_sink()
55                 source = self.get_source()
56
57                 #get the source coordinate
58                 x1, y1 = 0, 0
59                 connector_length = source.get_connector_length()
60                 if source.get_rotation() == 0:
61                         x1 = 0 + connector_length
62                 elif source.get_rotation() == 90:
63                         y1 = 0 - connector_length
64                 elif source.get_rotation() == 180:
65                         x1 = 0 - connector_length
66                 elif source.get_rotation() == 270:
67                         y1 = 0 + connector_length
68                 self.x1, self.y1 = x1, y1
69
70                 #get the sink coordinate
71                 x2, y2 = 0, 0
72                 connector_length = sink.get_connector_length() + CONNECTOR_ARROW_HEIGHT
73                 if sink.get_rotation() == 0:
74                         x2 = 0 - connector_length
75                 elif sink.get_rotation() == 90:
76                         y2 = 0 + connector_length
77                 elif sink.get_rotation() == 180:
78                         x2 = 0 + connector_length
79                 elif sink.get_rotation() == 270:
80                         y2 = 0 - connector_length
81                 self.x2, self.y2 = x2, y2
82
83                 #build the arrow
84                 if sink.get_rotation() == 0:
85                         self.arrow = [(0, 0), (0-CONNECTOR_ARROW_HEIGHT, 0-CONNECTOR_ARROW_BASE/2), (0-CONNECTOR_ARROW_HEIGHT, 0+CONNECTOR_ARROW_BASE/2)]
86                 elif sink.get_rotation() == 90:
87                         self.arrow = [(0, 0), (0-CONNECTOR_ARROW_BASE/2, 0+CONNECTOR_ARROW_HEIGHT), (0+CONNECTOR_ARROW_BASE/2, 0+CONNECTOR_ARROW_HEIGHT)]
88                 elif sink.get_rotation() == 180:
89                         self.arrow = [(0, 0), (0+CONNECTOR_ARROW_HEIGHT, 0-CONNECTOR_ARROW_BASE/2), (0+CONNECTOR_ARROW_HEIGHT, 0+CONNECTOR_ARROW_BASE/2)]
90                 elif sink.get_rotation() == 270:
91                         self.arrow = [(0, 0), (0-CONNECTOR_ARROW_BASE/2, 0-CONNECTOR_ARROW_HEIGHT), (0+CONNECTOR_ARROW_BASE/2, 0-CONNECTOR_ARROW_HEIGHT)]
92
93                 self._update_after_move()
94
95         def _update_after_move(self):
96                 """Calculate coordinates."""
97
98                 self.clear()
99
100                 #source connector
101                 source = self.get_source()
102                 X, Y = source.get_connector_coordinate()
103                 x1, y1 = self.x1 + X, self.y1 + Y
104                 self.add_line((x1, y1), (X, Y))
105
106                 #sink connector
107                 sink = self.get_sink()
108                 X, Y = sink.get_connector_coordinate()
109                 x2, y2 = self.x2 + X, self.y2 + Y
110                 self.add_line((x2, y2), (X, Y))
111
112                 #adjust arrow
113                 self._arrow = [(x+X, y+Y) for x,y in self.arrow]
114
115                 #add the horizontal and vertical lines in this connection
116                 if abs(source.get_connector_direction() - sink.get_connector_direction()) == 180:
117                         W = abs(x1 - x2)
118                         H = abs(y1 - y2)
119                         midX = (x1+x2)/2
120                         midY = (y1+y2)/2
121                         sW = x1 - x2
122                         if source.get_connector_direction() == 0: sW = sW * -1
123                         sH = y1 - y2
124                         if source.get_connector_direction() == 270: sH = sH * -1
125                         if ((W>H or sW<0) and self.is_horizontal(source.get_connector_direction())) or \
126                                 (W>=H and sH>=0 and self.is_vertical(source.get_connector_direction())):
127                                 self.add_line((x1,y1),(x1,midY))
128                                 self.add_line((x1,midY),(x2,midY))
129                                 self.add_line((x2,y2),(x2,midY))
130                         elif (H>=W and sW>=0 and self.is_horizontal(source.get_connector_direction())) or \
131                                 ((H>W or sH<0) and self.is_vertical(source.get_connector_direction())):
132                                 self.add_line((x1,y1),(midX,y1))
133                                 self.add_line((midX,y1),(midX,y2))
134                                 self.add_line((x2,y2),(midX,y2))
135                 else:
136                         #2 possible points to create a right-angled bend between the connectors
137                         points = [(x1, y2), (x2, y1)]
138                         #source connector -> points[0] should be in the direction of source (if possible)
139                         if Utils.get_angle_from_coordinates((x1, y1), points[0]) != source.get_connector_direction(): points.reverse()
140                         #points[0] -> sink connector should not be in the direction of sink
141                         if Utils.get_angle_from_coordinates(points[0], (x2, y2)) == sink.get_connector_direction(): points.reverse()
142                         #points[0] -> source connector should not be in the direction of source
143                         if Utils.get_angle_from_coordinates(points[0], (x1, y1)) == source.get_connector_direction(): points.reverse()
144                         #create right-angled connector
145                         self.add_line((x1, y1), points[0])
146                         self.add_line((x2, y2), points[0])
147
148         def draw(self, window):
149                 """!
150                 Draw the connection.
151                 @param window the gtk window to draw on
152                 """
153                 sink = self.get_sink()
154                 source = self.get_source()
155                 #check for changes
156                 if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation(): self.update()
157                 elif self._sink_coor != sink.get_coordinate() or self._source_coor != source.get_coordinate(): self._update_after_move()
158                 #cache values
159                 self._sink_rot = sink.get_rotation()
160                 self._source_rot = source.get_rotation()
161                 self._sink_coor = sink.get_coordinate()
162                 self._source_coor = source.get_coordinate()
163                 #draw
164                 fg_color = self.get_enabled() and Colors.FG_COLOR or Colors.DISABLED_FG_COLOR
165                 Element.draw(self, window, FG_color=fg_color)
166                 gc = self.get_gc()
167                 if self.is_valid(): gc.foreground = Colors.FG_COLOR
168                 else: gc.foreground = Colors.ERROR_COLOR
169                 #draw arrow on sink port
170                 window.draw_polygon(gc, True, self._arrow)