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.
23 #include "connpoint_line.h"
24 #include "connectionpoint.h"
27 #define DEBUG_PARENT 0
30 static void cpl_reorder_connections(ConnPointLine
*cpl
);
32 inline static ConnectionPoint
*new_connpoint(DiaObject
*obj
)
34 ConnectionPoint
*cp
= g_new0(ConnectionPoint
,1);
39 inline static void del_connpoint(ConnectionPoint
*cp
)
44 static ConnectionPoint
*
45 cpl_remove_connpoint(ConnPointLine
*cpl
,int pos
)
49 g_assert (cpl
->num_connections
> 0);
51 if (pos
>= cpl
->num_connections
) {
52 pos
= cpl
->num_connections
- 1;
54 while (pos
< 0) pos
+= cpl
->num_connections
;
57 cp
= (ConnectionPoint
*)(g_slist_nth(cpl
->connections
,pos
)->data
);
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 */
71 cpl_add_connectionpoint_at(ConnPointLine
*cpl
, int pos
,ConnectionPoint
*cp
)
74 /* special case handling so that the order of CPL groups in
75 the parent's CP list is preserved. */
78 g_assert(cpl
->connections
);
80 fcp
= (ConnectionPoint
*)(cpl
->connections
->data
);
82 for (i
=0; i
<cpl
->parent
->num_connections
; i
++) {
83 if (cpl
->parent
->connections
[i
] == fcp
) {
89 object_add_connectionpoint_at(cpl
->parent
,cp
,fpos
);
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
);
96 cpl
->connections
= g_slist_append(cpl
->connections
,(gpointer
)cp
);
99 cpl
->connections
= g_slist_insert(cpl
->connections
,(gpointer
)cp
,pos
);
101 cpl
->num_connections
++;
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). */
110 cpl_add_connectionpoint(ConnPointLine
*cpl
,ConnectionPoint
*cp
)
112 cpl_add_connectionpoint_at(cpl
,-1,cp
);
116 connpointline_create(DiaObject
*parent
, int num_connections
)
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
);
133 connpointline_destroy(ConnPointLine
*cpl
)
135 while (cpl
->num_connections
> 0) del_connpoint(cpl_remove_connpoint(cpl
,0));
139 static ConnPointLine
*
140 cpl_inplacecreate(DiaObject
*obj
, int nc
, int *realconncount
)
143 ConnPointLine
*newcpl
;
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
;
162 connpointline_load(DiaObject
*obj
,ObjectNode obj_node
,
163 const gchar
*name
, int default_nc
,int *realconncount
)
169 attr
= object_find_attribute(obj_node
, name
);
171 nc
= data_int(attribute_first_data(attr
));
172 cpl
= connpointline_create(obj
,nc
);
174 if (realconncount
) (*realconncount
) += cpl
->num_connections
;
177 return cpl_inplacecreate(obj,
178 load_int(obj_node,name,default_nc),
184 connpointline_save(ConnPointLine
*cpl
,ObjectNode obj_node
,
187 data_add_int(new_attribute(obj_node
, name
),cpl
->num_connections
);
191 connpointline_copy(DiaObject
*newobj
,ConnPointLine
*cpl
, int *realconncount
)
193 g_assert(realconncount
);
194 return cpl_inplacecreate(newobj
,cpl
->num_connections
,realconncount
);
197 void connpointline_update(ConnPointLine
*cpl
)
203 connpointline_putonaline(ConnPointLine
*cpl
,Point
*start
,Point
*end
)
206 real se_len
,pseudopoints
;
211 point_copy(&se_vector
, end
);
212 point_sub(&se_vector
, start
);
214 se_len
= point_len(&se_vector
);
217 point_normalize(&se_vector
);
222 if (fabs(se_vector
.x
) > fabs(se_vector
.y
))
223 dirs
= DIR_NORTH
|DIR_SOUTH
;
225 dirs
= DIR_EAST
|DIR_WEST
;
227 pseudopoints
= cpl
->num_connections
+ 1; /* here, we count the start and end
228 points as eating real positions. */
229 for (i
=0, elem
=cpl
->connections
;
230 i
<cpl
->num_connections
;
231 i
++,elem
=g_slist_next(elem
)) {
232 ConnectionPoint
*cp
= (ConnectionPoint
*)(elem
->data
);
234 cp
->directions
= dirs
;
235 point_scale(&cp
->pos
,se_len
* (i
+1.0)/pseudopoints
);
236 point_add(&cp
->pos
,start
);
241 /* These object_* functions are useful to me, because of what they do, I think
242 they belong to lib/object.c ; should I move them ? */
244 object_move_connection(DiaObject
*obj
,int sourcepos
,int destpos
)
247 g_assert(destpos
< sourcepos
);
248 cp
= obj
->connections
[sourcepos
];
250 memmove(&(obj
->connections
[destpos
+1]),&(obj
->connections
[destpos
]),
251 sizeof(ConnectionPoint
*)*(sourcepos
-destpos
));
252 obj
->connections
[destpos
] = cp
;
256 object_find_connection(DiaObject
*obj
, ConnectionPoint
*cp
, int startpos
)
259 for (i
= startpos
; i
< obj
->num_connections
; i
++) {
260 if (obj
->connections
[i
] == cp
) return i
;
262 return -1; /* should not happen */
267 static int obj_find_connection(DiaObject
*obj
,ConnectionPoint
*cp
)
270 for (i
=0;i
<obj
->num_connections
;i
++)
271 if (cp
== obj
->connections
[i
]) return i
;
276 static void cpl_dump_connections(ConnPointLine
*cpl
)
278 DiaObject
*obj
= cpl
->parent
;
283 g_message("CPL order dump");
284 for (i
=0,elem
= cpl
->connections
;
285 i
<cpl
->num_connections
;
286 i
++,elem
= g_slist_next(elem
)) {
287 cp
= (ConnectionPoint
*)(elem
->data
);
288 g_message("connection %p %d@CPL %d@OBJ",
289 cp
,i
,obj_find_connection(obj
,cp
));
295 cpl_reorder_connections(ConnPointLine
*cpl
)
297 /* This is needed, so that we don't mess up the loaded connections if
298 we save after the user has removed and added some connection points.
299 Normally, if an object owns several CPL, the order of the groups of
300 connectionpoints in its connectionpoint list should not change, as long
301 as we call this function whenever we do something.
303 The CPL has two big responsiblities here : first, it messes with
304 the parent object's structures (ugh), second, it must ensure that its
305 first CP is inserted so that it is found first in the parent's CP list,
306 and that the order of CP groups in the parent's CP list is respected (so
307 that the parent could have several different CPL and rely on the order).
315 if (!cpl
->connections
) return;
317 g_message("before cpl_reorder");
318 cpl_dump_connections(cpl
);
322 cp
= (ConnectionPoint
*)(cpl
->connections
->data
);
324 for (i
=0; i
<obj
->num_connections
; i
++){
325 if (obj
->connections
[i
] == cp
) {
330 g_assert(first
>= 0); /* otherwise things went loose badly. */
331 for (i
=0,j
=first
,elem
=cpl
->connections
;
332 i
<cpl
->num_connections
;
333 elem
=g_slist_next(elem
),i
++,j
++) {
334 cp
= (ConnectionPoint
*)(elem
->data
); /* = cpl->connections[i] */
335 if ( cp
!= obj
->connections
[j
]) { /* first time will always be false.
336 Is GCC that smart ? Probably not. */
337 object_move_connection(obj
,object_find_connection(obj
,cp
,j
),j
);
341 g_message("after cpl_reorder");
342 cpl_dump_connections(cpl
);
346 for (i
=0; i
<cpl
->parent
->num_connections
;i
++)
347 if (!cpl
->parent
->connections
[i
]) j
++;
348 /* We should never make such holes !*/
349 if (j
) g_warning("in cpl_reorder_connections there are %d holes in the parent's ConnectionPoint list !",j
);
356 connpointline_can_add_point(ConnPointLine
*cpl
, Point
*clicked
)
362 connpointline_can_remove_point(ConnPointLine
*cpl
, Point
*clicked
)
364 if (cpl
->num_connections
<= 1)
371 cpl_get_pointbefore(ConnPointLine
*cpl
, Point
*clickedpoint
)
379 if (!clickedpoint
) return 0;
381 for (i
=0,elem
=cpl
->connections
;
382 i
<cpl
->num_connections
;
383 i
++,elem
=g_slist_next(elem
)) {
384 cp
= (ConnectionPoint
*)(elem
->data
);
386 tmpdist
= distance_point_point(&cp
->pos
,clickedpoint
);
387 if (tmpdist
< dist
) {
392 tmpdist
= distance_point_point(&cpl
->end
,clickedpoint
);
393 if (tmpdist
< dist
) {
401 ObjectChange obj_change
;
403 int add
; /* How much to add or remove */
404 int applied
; /* 1 if the event has been applied. */
407 int pos
; /* Position where the change happened. */
408 ConnectionPoint
**cp
; /* The removed connection point. */
412 cpl_change_addremove(CPLChange
*change
, ConnPointLine
*cpl
,
413 int action
, int resultingapplied
)
416 if (action
> 0) { /* We should add */
418 cpl_add_connectionpoint_at(cpl
,change
->pos
,change
->cp
[action
]);
419 change
->cp
[action
] = NULL
;
421 cpl_reorder_connections(cpl
);
422 } else { /* We should remove. Warning, action is negative. */
424 change
->cp
[-action
] = cpl_remove_connpoint(cpl
,change
->pos
);
428 g_warning("cpl_change_addremove(): null action !");
430 change
->applied
= resultingapplied
;
434 cpl_change_apply(CPLChange
*change
, ConnPointLine
*probablynotcpl
)
436 cpl_change_addremove(change
,change
->cpl
,change
->add
,1);
440 cpl_change_revert(CPLChange
*change
, ConnPointLine
*probablynotcpl
)
442 cpl_change_addremove(change
,change
->cpl
,-(change
->add
),0);
445 static void cpl_change_free(CPLChange
*change
)
447 int i
= ABS(change
->add
);
451 del_connpoint(change
->cp
[i
]);
454 g_free(change
->cp
); change
->cp
= (ConnectionPoint
**)(0xDEADBEEF);
457 static ObjectChange
*
458 cpl_create_change(ConnPointLine
*cpl
, int pos
, int add
)
462 change
= g_new0(CPLChange
,1);
464 change
->obj_change
.apply
= (ObjectChangeApplyFunc
) cpl_change_apply
;
465 change
->obj_change
.revert
= (ObjectChangeRevertFunc
) cpl_change_revert
;
466 change
->obj_change
.free
= (ObjectChangeFreeFunc
) cpl_change_free
;
473 change
->cp
= g_malloc0(sizeof(ConnectionPoint
*) * ABS(add
));
475 change
->cp
[add
] = new_connpoint(cpl
->parent
);
478 return (ObjectChange
*)change
;
482 connpointline_add_points(ConnPointLine
*cpl
,
483 Point
*clickedpoint
, int count
)
486 ObjectChange
*change
;
488 pos
= cpl_get_pointbefore(cpl
,clickedpoint
);
489 change
= cpl_create_change(cpl
,pos
,count
);
491 change
->apply(change
, (DiaObject
*)cpl
);
497 connpointline_remove_points(ConnPointLine
*cpl
,
498 Point
*clickedpoint
, int count
)
501 ObjectChange
*change
;
503 pos
= cpl_get_pointbefore(cpl
,clickedpoint
);
504 change
= cpl_create_change(cpl
,pos
,-count
);
506 change
->apply(change
, (DiaObject
*)cpl
);
511 connpointline_adjust_count(ConnPointLine
*cpl
,
512 int newcount
, Point
*where
)
516 oldcount
= cpl
->num_connections
;
518 if (newcount
< 0) newcount
= 0;
520 delta
= newcount
- oldcount
;
522 ObjectChange
*change
;
523 /*g_message("going to adjust %d (to be %d)",delta,shouldbe);*/
526 change
= connpointline_add_points(cpl
, where
, delta
);
528 change
= connpointline_remove_points(cpl
, where
, -delta
);
530 if (change
->free
) change
->free(change
);
531 g_free(change
); /* we don't really need this change object. */