1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 /*! \file connection.c -- This file handles simple (straight-line) connection basics */
20 #include <config.h>
21 
22 #include <stdio.h>
23 #include <string.h> /* memcpy() */
24 #include <assert.h>
25 
26 #include "connection.h"
27 #include "message.h"
28 
29 /** Adjust connection endings for autogap.  This function actually moves the
30  * ends of the connection, but only when the end is connected to
31  * a mainpoint.
32  */
33 void
connection_adjust_for_autogap(Connection * connection)34 connection_adjust_for_autogap(Connection *connection)
35 {
36   Point endpoints[2];
37   ConnectionPoint *start_cp, *end_cp;
38 
39   start_cp = connection->endpoint_handles[0].connected_to;
40   end_cp = connection->endpoint_handles[1].connected_to;
41 
42   endpoints[0] = connection->endpoints[0];
43   endpoints[1] = connection->endpoints[1];
44 
45   if (connpoint_is_autogap(start_cp)) {
46     endpoints[0] = start_cp->pos;
47   }
48   if (connpoint_is_autogap(end_cp)) {
49     endpoints[1] = end_cp->pos;
50   }
51 
52   if (connpoint_is_autogap(start_cp)) {
53     connection->endpoints[0] = calculate_object_edge(&endpoints[0],
54 						     &endpoints[1],
55 						     start_cp->object);
56   }
57   if (connpoint_is_autogap(end_cp)) {
58     connection->endpoints[1] = calculate_object_edge(&endpoints[1],
59 						     &endpoints[0],
60 						     end_cp->object);
61   }
62 }
63 
64 /** Function called to move one of the handles associated with the
65  *  object.
66  *  This is an object_ops function.
67  * @param obj The object whose handle is being moved.
68  * @param handle The handle being moved.
69  * @param pos The position it has been moved to (corrected for
70  *   vertical/horizontal only movement).
71  * @param cp If non-NULL, the connectionpoint found at this position.
72  *   If @a cp is NULL, there may or may not be a connectionpoint.
73  * @param The reason the handle was moved.
74  *     - HANDLE_MOVE_USER means the user is dragging the point.
75  *     - HANDLE_MOVE_USER_FINAL means the user let go of the point.
76  *     - HANDLE_MOVE_CONNECTED means it was moved because something
77  *	    it was connected to moved.
78  * @param modifiers gives a bitset of modifier keys currently held down
79  *     - MODIFIER_SHIFT is either shift key
80  *     - MODIFIER_ALT is either alt key
81  *     - MODIFIER_CONTROL is either control key
82  *	    Each has MODIFIER_LEFT_* and MODIFIER_RIGHT_* variants
83  * @return An @a ObjectChange* with additional undo information, or
84  *  (in most cases) NULL.  Undo for moving the handle itself is handled
85  *  elsewhere.
86  */
87 ObjectChange*
connection_move_handle(Connection * conn,HandleId id,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)88 connection_move_handle(Connection *conn, HandleId id,
89 		       Point *to, ConnectionPoint *cp,
90 		       HandleMoveReason reason, ModifierKeys modifiers)
91 {
92   switch(id) {
93   case HANDLE_MOVE_STARTPOINT:
94     conn->endpoints[0] = *to;
95     break;
96   case HANDLE_MOVE_ENDPOINT:
97     conn->endpoints[1] = *to;
98     break;
99   default:
100     message_error("Internal error in connection_move_handle.\n");
101     break;
102   }
103   return NULL;
104 }
105 
106 /** Update the type and placement of the two handles.
107  * This only updates handles 0 and 1, not any handles added by subclasses.
108  * @param conn The connection object to do the update on.
109  */
110 void
connection_update_handles(Connection * conn)111 connection_update_handles(Connection *conn)
112 {
113   conn->endpoint_handles[0].id = HANDLE_MOVE_STARTPOINT;
114   conn->endpoint_handles[0].pos = conn->endpoints[0];
115 
116   conn->endpoint_handles[1].id = HANDLE_MOVE_ENDPOINT;
117   conn->endpoint_handles[1].pos = conn->endpoints[1];
118 }
119 
120 /** Update the bounding box for a connection.
121  * @param conn The connection to update bounding box on.
122  */
123 void
connection_update_boundingbox(Connection * conn)124 connection_update_boundingbox(Connection *conn)
125 {
126   assert(conn != NULL);
127 
128   line_bbox(&conn->endpoints[0],&conn->endpoints[1],
129             &conn->extra_spacing,&conn->object.bounding_box);
130 }
131 
132 /** Initialize a connection objects.
133  *  This will in turn call object_init.
134  * @param conn A newly allocated connection object.
135  * @param num_handles How many handles to allocate room for.
136  * @param num_connections How many connectionpoints to allocate room for.
137  */
138 void
connection_init(Connection * conn,int num_handles,int num_connections)139 connection_init(Connection *conn, int num_handles, int num_connections)
140 {
141   DiaObject *obj;
142   int i;
143 
144   obj = &conn->object;
145 
146   assert(num_handles>=2);
147 
148   object_init(obj, num_handles, num_connections);
149 
150   assert(obj->handles!=NULL);
151 
152   for (i=0;i<2;i++) {
153     obj->handles[i] = &conn->endpoint_handles[i];
154     obj->handles[i]->type = HANDLE_MAJOR_CONTROL;
155     obj->handles[i]->connect_type = HANDLE_CONNECTABLE;
156     obj->handles[i]->connected_to = NULL;
157   }
158 }
159 
160 /** Copies an object connection-level and down.
161  * @param from The object to copy from.
162  * @param to The newly allocated object to copy into.
163  */
164 void
connection_copy(Connection * from,Connection * to)165 connection_copy(Connection *from, Connection *to)
166 {
167   int i;
168   DiaObject *toobj;
169   DiaObject *fromobj;
170 
171   toobj = &to->object;
172   fromobj = &from->object;
173 
174   object_copy(fromobj, toobj);
175 
176   for (i=0;i<2;i++) {
177     to->endpoints[i] = from->endpoints[i];
178   }
179 
180   for (i=0;i<2;i++) {
181     /* handles: */
182     to->endpoint_handles[i] = from->endpoint_handles[i];
183     to->endpoint_handles[i].connected_to = NULL;
184     toobj->handles[i] = &to->endpoint_handles[i];
185   }
186   memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
187 }
188 
189 /** Function called before an object is deleted.
190  *  This function must call the parent class's DestroyFunc, and then free
191  *  the memory associated with the object, but not the object itself
192  *  Must also unconnect itself from all other objects, which can be done
193  *  by calling object_destroy, or letting the super-class call it)
194  *  This is one of the object_ops functions.
195  * @param conn An object to destroy.
196  */
197 void
connection_destroy(Connection * conn)198 connection_destroy(Connection *conn)
199 {
200   object_destroy(&conn->object);
201 }
202 
203 /** Save a connections data to XML.
204  * @param conn The connection to save.
205  * @param obj_node The XML node to save it to.
206  */
207 void
connection_save(Connection * conn,ObjectNode obj_node)208 connection_save(Connection *conn, ObjectNode obj_node)
209 {
210   AttributeNode attr;
211 
212   object_save(&conn->object, obj_node);
213 
214   attr = new_attribute(obj_node, "conn_endpoints");
215   data_add_point(attr, &conn->endpoints[0]);
216   data_add_point(attr, &conn->endpoints[1]);
217 }
218 
219 /** Load a connections data from XML.
220  * @param conn A fresh connection object to load into.
221  * @param obj_node The XML node to load from.
222  */
223 void
connection_load(Connection * conn,ObjectNode obj_node)224 connection_load(Connection *conn, ObjectNode obj_node)
225 {
226   AttributeNode attr;
227   DataNode data;
228 
229   object_load(&conn->object, obj_node);
230 
231   attr = object_find_attribute(obj_node, "conn_endpoints");
232   if (attr != NULL) {
233     data = attribute_first_data(attr);
234     data_point( data , &conn->endpoints[0] );
235     data = data_next(data);
236     data_point( data , &conn->endpoints[1] );
237   }
238 }
239