#ifdef'd xmlerror.h
[dia.git] / lib / connpoint_line.c
blob6037aab4bb4d5937e523dcd31403c31d033aee52
1 /*
2 * SADT diagram support for dia
3 * Copyright(C) 2000 Cyrille Chepelov
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program 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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #include <config.h>
22 #include <string.h>
23 #include "connpoint_line.h"
24 #include "connectionpoint.h"
25 #include "dia_xml.h"
27 #define DEBUG_PARENT 0
28 #define DEBUG_ORDER 0
30 static void cpl_reorder_connections(ConnPointLine *cpl);
32 inline static ConnectionPoint *new_connpoint(Object *obj)
34 ConnectionPoint *cp = g_new0(ConnectionPoint,1);
35 cp->object = obj;
36 return cp;
39 inline static void del_connpoint(ConnectionPoint *cp)
41 g_free(cp);
44 static ConnectionPoint *
45 cpl_remove_connpoint(ConnPointLine *cpl,int pos)
47 ConnectionPoint *cp;
49 g_assert (cpl->num_connections > 0);
51 if (pos >= cpl->num_connections) {
52 pos = cpl->num_connections - 1;
53 } else {
54 while (pos < 0) pos += cpl->num_connections;
57 cp = (ConnectionPoint *)(g_slist_nth(cpl->connections,pos)->data);
58 g_assert(cp);
60 cpl->connections = g_slist_remove(cpl->connections,(gpointer)cp);
61 object_remove_connectionpoint(cpl->parent,cp);
63 cpl->num_connections--;
64 /* removing a point doesn't change the order of the remaining ones, so we
65 don't need to call cpl_reorder_connections. */
66 /* The caller is responsible for freeing the removed connection point */
67 return cp;
70 static void
71 cpl_add_connectionpoint_at(ConnPointLine *cpl, int pos,ConnectionPoint *cp)
73 if (pos == 0) {
74 /* special case handling so that the order of CPL groups in
75 the parent's CP list is preserved. */
76 int fpos,i;
77 ConnectionPoint *fcp;
78 g_assert(cpl->connections);
79 fpos = -1;
80 fcp = (ConnectionPoint *)(cpl->connections->data);
81 g_assert(fcp);
82 for (i=0; i<cpl->parent->num_connections; i++) {
83 if (cpl->parent->connections[i] == fcp) {
84 fpos = i;
85 break;
88 g_assert(fpos >= 0);
89 object_add_connectionpoint_at(cpl->parent,cp,fpos);
90 }else {
91 /* XXX : make this a little better ; try to insert at the correct
92 position right away to eliminate cpl_reorder_connection */
93 object_add_connectionpoint(cpl->parent,cp);
95 if (pos < 0) {
96 cpl->connections = g_slist_append(cpl->connections,(gpointer)cp);
98 else {
99 cpl->connections = g_slist_insert(cpl->connections,(gpointer)cp,pos);
101 cpl->num_connections++;
103 /* we should call
104 cpl_reorder_connections(cpl);
105 before we leave the object !! However, this is delayed, for the case
106 several CP's are added at once (initialisation). */
109 inline static void
110 cpl_add_connectionpoint(ConnPointLine *cpl,ConnectionPoint *cp)
112 cpl_add_connectionpoint_at(cpl,-1,cp);
115 ConnPointLine *
116 connpointline_create(Object *parent, int num_connections)
118 ConnPointLine *cpl;
119 int i;
121 cpl = g_new0(ConnPointLine,1);
122 cpl->parent = parent;
124 cpl->connections = NULL;
125 for (i=0; i<num_connections; i++) {
126 cpl_add_connectionpoint(cpl,new_connpoint(cpl->parent));
128 cpl_reorder_connections(cpl);
129 return cpl;
132 void
133 connpointline_destroy(ConnPointLine *cpl)
135 while (cpl->num_connections > 0) del_connpoint(cpl_remove_connpoint(cpl,0));
136 g_free(cpl);
139 static ConnPointLine *
140 cpl_inplacecreate(Object *obj, int nc, int *realconncount)
142 int i;
143 ConnPointLine *newcpl;
144 ConnectionPoint *cp;
146 /* This thing creates a connection point line without actually adding
147 connection points to the parent object. */
148 newcpl = g_new0(ConnPointLine,1);
149 newcpl->parent = obj;
151 for (i=0; i < nc; i++,(*realconncount)++) {
152 cp = g_new0(ConnectionPoint,1);
153 cp->object = newcpl->parent;
154 obj->connections[*realconncount] = cp;
155 newcpl->connections = g_slist_append(newcpl->connections,cp);
157 newcpl->num_connections = nc;
158 return newcpl;
161 ConnPointLine *
162 connpointline_load(Object *obj,ObjectNode obj_node,
163 const gchar *name, int default_nc,int *realconncount)
165 ConnPointLine *cpl;
166 int nc = default_nc;
167 AttributeNode attr;
169 attr = object_find_attribute(obj_node, name);
170 if (attr != NULL)
171 nc = data_int(attribute_first_data(attr));
172 cpl = connpointline_create(obj,nc);
174 if (realconncount) (*realconncount) += cpl->num_connections;
175 return cpl;
176 /* NOT this !
177 return cpl_inplacecreate(obj,
178 load_int(obj_node,name,default_nc),
179 realconncount);
183 void
184 connpointline_save(ConnPointLine *cpl,ObjectNode obj_node,
185 const gchar *name)
187 data_add_int(new_attribute(obj_node, name),cpl->num_connections);
190 ConnPointLine *
191 connpointline_copy(Object *newobj,ConnPointLine *cpl, int *realconncount)
193 g_assert(realconncount);
194 return cpl_inplacecreate(newobj,cpl->num_connections,realconncount);
197 void connpointline_update(ConnPointLine *cpl)
202 void
203 connpointline_putonaline(ConnPointLine *cpl,Point *start,Point *end)
205 Point se_vector;
206 real se_len,pseudopoints;
207 int i;
208 GSList *elem;
210 point_copy(&se_vector, end);
211 point_sub(&se_vector, start);
213 se_len = point_len(&se_vector);
215 if (se_len > 0)
216 point_normalize(&se_vector);
218 cpl->start = *start;
219 cpl->end = *end;
221 pseudopoints = cpl->num_connections + 1; /* here, we count the start and end
222 points as eating real positions. */
223 for (i=0, elem=cpl->connections;
224 i<cpl->num_connections;
225 i++,elem=g_slist_next(elem)) {
226 ConnectionPoint *cp = (ConnectionPoint *)(elem->data);
227 cp->pos = se_vector;
228 point_scale(&cp->pos,se_len * (i+1.0)/pseudopoints);
229 point_add(&cp->pos,start);
234 /* These object_* functions are useful to me, because of what they do, I think
235 they belong to lib/object.c ; should I move them ? */
236 static void
237 object_move_connection(Object *obj,int sourcepos,int destpos)
239 ConnectionPoint *cp;
240 g_assert(destpos < sourcepos);
241 cp = obj->connections[sourcepos];
243 memmove(&(obj->connections[destpos+1]),&(obj->connections[destpos]),
244 sizeof(ConnectionPoint *)*(sourcepos-destpos));
245 obj->connections[destpos] = cp;
248 static int
249 object_find_connection(Object *obj, ConnectionPoint *cp, int startpos)
251 int i;
252 for (i = startpos; i < obj->num_connections; i++) {
253 if (obj->connections[i] == cp) return i;
255 return -1; /* should not happen */
259 #if DEBUG_ORDER
260 static int obj_find_connection(Object *obj,ConnectionPoint *cp)
262 int i;
263 for (i=0;i<obj->num_connections;i++)
264 if (cp == obj->connections[i]) return i;
265 return -1;
269 static void cpl_dump_connections(ConnPointLine *cpl)
271 Object *obj = cpl->parent;
272 int i;
273 GSList *elem;
274 ConnectionPoint *cp;
276 g_message("CPL order dump");
277 for (i=0,elem = cpl->connections;
278 i<cpl->num_connections;
279 i++,elem = g_slist_next(elem)) {
280 cp = (ConnectionPoint *)(elem->data);
281 g_message("connection %p %d@CPL %d@OBJ",
282 cp,i,obj_find_connection(obj,cp));
285 #endif
287 static void
288 cpl_reorder_connections(ConnPointLine *cpl)
290 /* This is needed, so that we don't mess up the loaded connections if
291 we save after the user has removed and added some connection points.
292 Normally, if an object owns several CPL, the order of the groups of
293 connectionpoints in its connectionpoint list should not change, as long
294 as we call this function whenever we do something.
296 The CPL has two big responsiblities here : first, it messes with
297 the parent object's structures (ugh), second, it must ensure that its
298 first CP is inserted so that it is found first in the parent's CP list,
299 and that the order of CP groups in the parent's CP list is respected (so
300 that the parent could have several different CPL and rely on the order).
303 int i,j,first;
304 ConnectionPoint *cp;
305 GSList *elem;
306 Object *obj;
308 if (!cpl->connections) return;
309 #if DEBUG_ORDER
310 g_message("before cpl_reorder");
311 cpl_dump_connections(cpl);
312 #endif
314 first = -1;
315 cp = (ConnectionPoint *)(cpl->connections->data);
316 obj = cpl->parent;
317 for (i=0; i<obj->num_connections; i++){
318 if (obj->connections[i] == cp) {
319 first = i;
320 break;
323 g_assert(first >= 0); /* otherwise things went loose badly. */
324 for (i=0,j=first,elem=cpl->connections;
325 i<cpl->num_connections;
326 elem=g_slist_next(elem),i++,j++) {
327 cp = (ConnectionPoint *)(elem->data); /* = cpl->connections[i] */
328 if ( cp != obj->connections[j]) { /* first time will always be false.
329 Is GCC that smart ? Probably not. */
330 object_move_connection(obj,object_find_connection(obj,cp,j),j);
333 #if DEBUG_ORDER
334 g_message("after cpl_reorder");
335 cpl_dump_connections(cpl);
336 #endif
337 #if DEBUG_PARENT
338 j = 0;
339 for (i=0; i<cpl->parent->num_connections;i++)
340 if (!cpl->parent->connections[i]) j++;
341 /* We should never make such holes !*/
342 if (j) g_warning("in cpl_reorder_connections there are %d holes in the parent's ConnectionPoint list !",j);
343 #endif
348 int
349 connpointline_can_add_point(ConnPointLine *cpl, Point *clicked)
351 return 1;
355 connpointline_can_remove_point(ConnPointLine *cpl, Point *clicked)
357 if (cpl->num_connections <= 1)
358 return 0;
359 else
360 return 1;
363 static int
364 cpl_get_pointbefore(ConnPointLine *cpl, Point *clickedpoint)
366 int i, pos = -1;
367 GSList *elem;
368 ConnectionPoint *cp;
369 real dist = 65536.0;
370 real tmpdist;
372 if (!clickedpoint) return 0;
374 for (i=0,elem=cpl->connections;
375 i<cpl->num_connections;
376 i++,elem=g_slist_next(elem)) {
377 cp = (ConnectionPoint *)(elem->data);
379 tmpdist = distance_point_point(&cp->pos,clickedpoint);
380 if (tmpdist < dist) {
381 dist = tmpdist;
382 pos = i;
385 tmpdist = distance_point_point(&cpl->end,clickedpoint);
386 if (tmpdist < dist) {
387 /*dist = tmpdist; */
388 pos = -1;
390 return pos;
393 typedef struct {
394 ObjectChange obj_change;
396 int add; /* How much to add or remove */
397 int applied; /* 1 if the event has been applied. */
399 ConnPointLine *cpl;
400 int pos; /* Position where the change happened. */
401 ConnectionPoint **cp; /* The removed connection point. */
402 } CPLChange;
404 static void
405 cpl_change_addremove(CPLChange *change, ConnPointLine *cpl,
406 int action, int resultingapplied)
408 if (action != 0) {
409 if (action > 0) { /* We should add */
410 while (action--) {
411 cpl_add_connectionpoint_at(cpl,change->pos,change->cp[action]);
412 change->cp[action] = NULL;
414 cpl_reorder_connections(cpl);
415 } else { /* We should remove. Warning, action is negative. */
416 while (action++) {
417 change->cp[-action] = cpl_remove_connpoint(cpl,change->pos);
420 } else {
421 g_warning("cpl_change_addremove(): null action !");
423 change->applied = resultingapplied;
426 static void
427 cpl_change_apply(CPLChange *change, ConnPointLine *probablynotcpl)
429 cpl_change_addremove(change,change->cpl,change->add,1);
432 static void
433 cpl_change_revert(CPLChange *change, ConnPointLine *probablynotcpl)
435 cpl_change_addremove(change,change->cpl,-(change->add),0);
438 static void cpl_change_free(CPLChange *change)
440 int i = ABS(change->add);
442 while (i--) {
443 if (change->cp[i]) {
444 del_connpoint(change->cp[i]);
447 g_free(change->cp); change->cp = (ConnectionPoint **)(0xDEADBEEF);
450 static ObjectChange *
451 cpl_create_change(ConnPointLine *cpl, int pos, int add)
453 CPLChange *change;
455 change = g_new0(CPLChange,1);
457 change->obj_change.apply = (ObjectChangeApplyFunc) cpl_change_apply;
458 change->obj_change.revert = (ObjectChangeRevertFunc) cpl_change_revert;
459 change->obj_change.free = (ObjectChangeFreeFunc) cpl_change_free;
461 change->cpl = cpl;
462 change->applied = 0;
463 change->add = add;
464 change->pos = pos;
466 change->cp = g_malloc0(sizeof(ConnectionPoint *) * ABS(add));
467 while (add-- > 0) {
468 change->cp[add] = new_connpoint(cpl->parent);
471 return (ObjectChange *)change;
474 ObjectChange *
475 connpointline_add_points(ConnPointLine *cpl,
476 Point *clickedpoint, int count)
478 int pos;
479 ObjectChange *change;
481 pos = cpl_get_pointbefore(cpl,clickedpoint);
482 change = cpl_create_change(cpl,pos,count);
484 change->apply(change, (Object *)cpl);
485 return change;
489 ObjectChange *
490 connpointline_remove_points(ConnPointLine *cpl,
491 Point *clickedpoint, int count)
493 int pos;
494 ObjectChange *change;
496 pos = cpl_get_pointbefore(cpl,clickedpoint);
497 change = cpl_create_change(cpl,pos,-count);
499 change->apply(change, (Object *)cpl);
500 return change;
503 int
504 connpointline_adjust_count(ConnPointLine *cpl,
505 int newcount, Point *where)
507 int oldcount,delta;
509 oldcount = cpl->num_connections;
511 if (newcount < 0) newcount = 0;
513 delta = newcount - oldcount;
514 if (delta != 0) {
515 ObjectChange *change;
516 /*g_message("going to adjust %d (to be %d)",delta,shouldbe);*/
518 if (delta > 0) {
519 change = connpointline_add_points(cpl, where, delta);
520 } else {
521 change = connpointline_remove_points(cpl, where, -delta);
523 if (change->free) change->free(change);
524 g_free(change); /* we don't really need this change object. */
528 return oldcount;