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(Object
*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(Object
*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(Object
*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(Object
*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(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
)
203 connpointline_putonaline(ConnPointLine
*cpl
,Point
*start
,Point
*end
)
206 real se_len
,pseudopoints
;
210 point_copy(&se_vector
, end
);
211 point_sub(&se_vector
, start
);
213 se_len
= point_len(&se_vector
);
216 point_normalize(&se_vector
);
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
);
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 ? */
237 object_move_connection(Object
*obj
,int sourcepos
,int destpos
)
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
;
249 object_find_connection(Object
*obj
, ConnectionPoint
*cp
, int startpos
)
252 for (i
= startpos
; i
< obj
->num_connections
; i
++) {
253 if (obj
->connections
[i
] == cp
) return i
;
255 return -1; /* should not happen */
260 static int obj_find_connection(Object
*obj
,ConnectionPoint
*cp
)
263 for (i
=0;i
<obj
->num_connections
;i
++)
264 if (cp
== obj
->connections
[i
]) return i
;
269 static void cpl_dump_connections(ConnPointLine
*cpl
)
271 Object
*obj
= cpl
->parent
;
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
));
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).
308 if (!cpl
->connections
) return;
310 g_message("before cpl_reorder");
311 cpl_dump_connections(cpl
);
315 cp
= (ConnectionPoint
*)(cpl
->connections
->data
);
317 for (i
=0; i
<obj
->num_connections
; i
++){
318 if (obj
->connections
[i
] == cp
) {
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
);
334 g_message("after cpl_reorder");
335 cpl_dump_connections(cpl
);
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
);
349 connpointline_can_add_point(ConnPointLine
*cpl
, Point
*clicked
)
355 connpointline_can_remove_point(ConnPointLine
*cpl
, Point
*clicked
)
357 if (cpl
->num_connections
<= 1)
364 cpl_get_pointbefore(ConnPointLine
*cpl
, Point
*clickedpoint
)
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
) {
385 tmpdist
= distance_point_point(&cpl
->end
,clickedpoint
);
386 if (tmpdist
< dist
) {
394 ObjectChange obj_change
;
396 int add
; /* How much to add or remove */
397 int applied
; /* 1 if the event has been applied. */
400 int pos
; /* Position where the change happened. */
401 ConnectionPoint
**cp
; /* The removed connection point. */
405 cpl_change_addremove(CPLChange
*change
, ConnPointLine
*cpl
,
406 int action
, int resultingapplied
)
409 if (action
> 0) { /* We should add */
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. */
417 change
->cp
[-action
] = cpl_remove_connpoint(cpl
,change
->pos
);
421 g_warning("cpl_change_addremove(): null action !");
423 change
->applied
= resultingapplied
;
427 cpl_change_apply(CPLChange
*change
, ConnPointLine
*probablynotcpl
)
429 cpl_change_addremove(change
,change
->cpl
,change
->add
,1);
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
);
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
)
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
;
466 change
->cp
= g_malloc0(sizeof(ConnectionPoint
*) * ABS(add
));
468 change
->cp
[add
] = new_connpoint(cpl
->parent
);
471 return (ObjectChange
*)change
;
475 connpointline_add_points(ConnPointLine
*cpl
,
476 Point
*clickedpoint
, int count
)
479 ObjectChange
*change
;
481 pos
= cpl_get_pointbefore(cpl
,clickedpoint
);
482 change
= cpl_create_change(cpl
,pos
,count
);
484 change
->apply(change
, (Object
*)cpl
);
490 connpointline_remove_points(ConnPointLine
*cpl
,
491 Point
*clickedpoint
, int count
)
494 ObjectChange
*change
;
496 pos
= cpl_get_pointbefore(cpl
,clickedpoint
);
497 change
= cpl_create_change(cpl
,pos
,-count
);
499 change
->apply(change
, (Object
*)cpl
);
504 connpointline_adjust_count(ConnPointLine
*cpl
,
505 int newcount
, Point
*where
)
509 oldcount
= cpl
->num_connections
;
511 if (newcount
< 0) newcount
= 0;
513 delta
= newcount
- oldcount
;
515 ObjectChange
*change
;
516 /*g_message("going to adjust %d (to be %d)",delta,shouldbe);*/
519 change
= connpointline_add_points(cpl
, where
, delta
);
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. */