1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
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.
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.
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.
25 #include <string.h> /* memcpy() */
27 #include "poly_conn.h"
29 #include "diarenderer.h"
37 ObjectChange obj_change
;
39 enum change_type type
;
45 Handle
*handle
; /* owning ref when not applied for ADD_POINT
46 owning ref when applied for REMOVE_POINT */
47 ConnectionPoint
*connected_to
; /* NULL if not connected */
51 polyconn_create_change(PolyConn
*poly
, enum change_type type
,
52 Point
*point
, int segment
, Handle
*handle
,
53 ConnectionPoint
*connected_to
);
63 setup_handle(Handle
*handle
, PolyConnHandleType t
)
65 handle
->id
= (PC_HANDLE_CORNER
== t
? HANDLE_CORNER
66 : (PC_HANDLE_END
== t
? HANDLE_MOVE_ENDPOINT
67 : HANDLE_MOVE_STARTPOINT
));
68 handle
->type
= (PC_HANDLE_CORNER
== t
? HANDLE_MINOR_CONTROL
: HANDLE_MAJOR_CONTROL
);
69 handle
->connect_type
= HANDLE_CONNECTABLE
;
70 handle
->connected_to
= NULL
;
74 static int get_handle_nr(PolyConn
*poly
, Handle
*handle
)
77 for (i
=0;i
<poly
->numpoints
;i
++) {
78 if (poly
->object
.handles
[i
] == handle
)
85 polyconn_move_handle(PolyConn
*poly
, Handle
*handle
,
86 Point
*to
, ConnectionPoint
*cp
,
87 HandleMoveReason reason
, ModifierKeys modifiers
)
91 handle_nr
= get_handle_nr(poly
, handle
);
93 case HANDLE_MOVE_STARTPOINT
:
94 poly
->points
[0] = *to
;
96 case HANDLE_MOVE_ENDPOINT
:
97 poly
->points
[poly
->numpoints
-1] = *to
;
100 poly
->points
[handle_nr
] = *to
;
103 message_error("Internal error in polyconn_move_handle.\n");
111 polyconn_move(PolyConn
*poly
, Point
*to
)
117 point_sub(&p
, &poly
->points
[0]);
119 poly
->points
[0] = *to
;
120 for (i
=1;i
<poly
->numpoints
;i
++) {
121 point_add(&poly
->points
[i
], &p
);
128 polyconn_closest_segment(PolyConn
*poly
, Point
*point
, real line_width
)
134 dist
= distance_line_point( &poly
->points
[0], &poly
->points
[1],
137 for (i
=1;i
<poly
->numpoints
-1;i
++) {
139 distance_line_point( &poly
->points
[i
], &poly
->points
[i
+1],
141 if (new_dist
< dist
) {
150 polyconn_closest_handle(PolyConn
*poly
, Point
*point
)
156 closest
= poly
->object
.handles
[0];
157 dist
= distance_point_point( point
, &closest
->pos
);
158 for (i
=1;i
<poly
->numpoints
;i
++) {
160 new_dist
= distance_point_point( point
, &poly
->points
[i
]);
161 if (new_dist
< dist
) {
163 closest
= poly
->object
.handles
[i
];
170 polyconn_distance_from(PolyConn
*poly
, Point
*point
, real line_width
)
175 dist
= distance_line_point( &poly
->points
[0], &poly
->points
[1],
177 for (i
=1;i
<poly
->numpoints
-1;i
++) {
179 distance_line_point( &poly
->points
[i
], &poly
->points
[i
+1],
186 add_handle(PolyConn
*poly
, int pos
, Point
*point
, Handle
*handle
)
192 poly
->points
= g_realloc(poly
->points
, poly
->numpoints
*sizeof(Point
));
194 for (i
=poly
->numpoints
-1; i
> pos
; i
--) {
195 poly
->points
[i
] = poly
->points
[i
-1];
197 poly
->points
[pos
] = *point
;
198 object_add_handle_at((DiaObject
*)poly
, handle
, pos
);
200 obj
= (DiaObject
*)poly
;
202 obj
->handles
[1]->type
= HANDLE_MINOR_CONTROL
;
203 obj
->handles
[1]->id
= HANDLE_CORNER
;
205 if (pos
==obj
->num_handles
-1) {
206 obj
->handles
[obj
->num_handles
-2]->type
= HANDLE_MINOR_CONTROL
;
207 obj
->handles
[obj
->num_handles
-2]->id
= HANDLE_CORNER
;
212 remove_handle(PolyConn
*poly
, int pos
)
218 obj
= (DiaObject
*)poly
;
221 obj
->handles
[1]->type
= HANDLE_MAJOR_CONTROL
;
222 obj
->handles
[1]->id
= HANDLE_MOVE_STARTPOINT
;
224 if (pos
==obj
->num_handles
-1) {
225 obj
->handles
[obj
->num_handles
-2]->type
= HANDLE_MAJOR_CONTROL
;
226 obj
->handles
[obj
->num_handles
-2]->id
= HANDLE_MOVE_ENDPOINT
;
229 /* delete the points */
231 for (i
=pos
; i
< poly
->numpoints
; i
++) {
232 poly
->points
[i
] = poly
->points
[i
+1];
234 poly
->points
= g_realloc(poly
->points
, poly
->numpoints
*sizeof(Point
));
236 old_handle
= obj
->handles
[pos
];
237 object_remove_handle(&poly
->object
, old_handle
);
241 /* Add a point by splitting segment into two, putting the new point at
242 'point' or, if NULL, in the middle */
244 polyconn_add_point(PolyConn
*poly
, int segment
, Point
*point
)
250 realpoint
.x
= (poly
->points
[segment
].x
+poly
->points
[segment
+1].x
)/2;
251 realpoint
.y
= (poly
->points
[segment
].y
+poly
->points
[segment
+1].y
)/2;
256 new_handle
= g_malloc(sizeof(Handle
));
257 setup_handle(new_handle
, PC_HANDLE_CORNER
);
258 add_handle(poly
, segment
+1, &realpoint
, new_handle
);
259 return polyconn_create_change(poly
, TYPE_ADD_POINT
,
260 &realpoint
, segment
+1, new_handle
,
265 polyconn_remove_point(PolyConn
*poly
, int pos
)
268 ConnectionPoint
*connectionpoint
;
271 old_handle
= poly
->object
.handles
[pos
];
272 old_point
= poly
->points
[pos
];
273 connectionpoint
= old_handle
->connected_to
;
275 object_unconnect((DiaObject
*)poly
, old_handle
);
277 remove_handle(poly
, pos
);
279 polyconn_update_data(poly
);
281 return polyconn_create_change(poly
, TYPE_REMOVE_POINT
,
282 &old_point
, pos
, old_handle
,
287 polyconn_update_data(PolyConn
*poly
)
290 DiaObject
*obj
= &poly
->object
;
292 /* handle the case of whole points array update (via set_prop) */
293 if (poly
->numpoints
!= obj
->num_handles
) {
294 g_assert(0 == obj
->num_connections
);
296 obj
->handles
= g_realloc(obj
->handles
,
297 poly
->numpoints
*sizeof(Handle
*));
298 obj
->num_handles
= poly
->numpoints
;
299 for (i
=0;i
<poly
->numpoints
;i
++) {
300 obj
->handles
[i
] = g_malloc(sizeof(Handle
));
302 setup_handle(obj
->handles
[i
], PC_HANDLE_START
);
303 else if (i
== poly
->numpoints
-1)
304 setup_handle(obj
->handles
[i
], PC_HANDLE_END
);
306 setup_handle(obj
->handles
[i
], PC_HANDLE_CORNER
);
310 /* Update handles: */
311 for (i
=0;i
<poly
->numpoints
;i
++) {
312 obj
->handles
[i
]->pos
= poly
->points
[i
];
317 polyconn_update_boundingbox(PolyConn
*poly
)
319 assert(poly
!= NULL
);
321 polyline_bbox(&poly
->points
[0],
323 &poly
->extra_spacing
, FALSE
,
324 &poly
->object
.bounding_box
);
328 polyconn_simple_draw(PolyConn
*poly
, DiaRenderer
*renderer
, real width
)
332 assert(poly
!= NULL
);
333 assert(renderer
!= NULL
);
335 points
= &poly
->points
[0];
337 DIA_RENDERER_GET_CLASS(renderer
)->set_linewidth(renderer
, width
);
338 DIA_RENDERER_GET_CLASS(renderer
)->set_linestyle(renderer
, LINESTYLE_SOLID
);
339 DIA_RENDERER_GET_CLASS(renderer
)->set_linejoin(renderer
, LINEJOIN_ROUND
);
340 DIA_RENDERER_GET_CLASS(renderer
)->set_linecaps(renderer
, LINECAPS_BUTT
);
342 DIA_RENDERER_GET_CLASS(renderer
)->draw_polyline(renderer
, points
, poly
->numpoints
,
348 polyconn_init(PolyConn
*poly
, int num_points
)
355 object_init(obj
, num_points
, 0);
357 poly
->numpoints
= num_points
;
359 poly
->points
= g_malloc(num_points
*sizeof(Point
));
361 for (i
=0;i
<num_points
;i
++) {
362 obj
->handles
[i
] = g_malloc(sizeof(Handle
));
364 setup_handle(obj
->handles
[i
], PC_HANDLE_START
);
365 else if (i
== num_points
-1)
366 setup_handle(obj
->handles
[i
], PC_HANDLE_END
);
368 setup_handle(obj
->handles
[i
], PC_HANDLE_CORNER
);
371 polyconn_update_data(poly
);
374 /** This function does *not* set up handles */
376 polyconn_set_points(PolyConn
*poly
, int num_points
, Point
*points
)
380 poly
->numpoints
= num_points
;
383 g_free(poly
->points
);
385 poly
->points
= g_malloc((poly
->numpoints
)*sizeof(Point
));
387 for (i
=0;i
<poly
->numpoints
;i
++) {
388 poly
->points
[i
] = points
[i
];
393 polyconn_copy(PolyConn
*from
, PolyConn
*to
)
396 DiaObject
*toobj
, *fromobj
;
399 fromobj
= &from
->object
;
401 object_copy(fromobj
, toobj
);
403 to
->object
.handles
[0] = g_new(Handle
,1);
404 *to
->object
.handles
[0] = *from
->object
.handles
[0];
406 for (i
=1;i
<toobj
->num_handles
-1;i
++) {
407 to
->object
.handles
[i
] = g_malloc(sizeof(Handle
));
408 setup_handle(to
->object
.handles
[i
], PC_HANDLE_CORNER
);
411 to
->object
.handles
[toobj
->num_handles
-1] = g_new(Handle
,1);
412 *to
->object
.handles
[toobj
->num_handles
-1] =
413 *from
->object
.handles
[toobj
->num_handles
-1];
414 polyconn_set_points(to
, from
->numpoints
, from
->points
);
416 memcpy(&to
->extra_spacing
,&from
->extra_spacing
,sizeof(to
->extra_spacing
));
417 polyconn_update_data(to
);
421 polyconn_destroy(PolyConn
*poly
)
424 Handle
**temp_handles
;
426 /* Need to store these temporary since object.handles is
427 freed by object_destroy() */
428 temp_handles
= g_new(Handle
*, poly
->numpoints
);
429 for (i
=0;i
<poly
->numpoints
;i
++)
430 temp_handles
[i
] = poly
->object
.handles
[i
];
432 object_destroy(&poly
->object
);
434 for (i
=0;i
<poly
->numpoints
;i
++)
435 g_free(temp_handles
[i
]);
436 g_free(temp_handles
);
438 g_free(poly
->points
);
443 polyconn_save(PolyConn
*poly
, ObjectNode obj_node
)
448 object_save(&poly
->object
, obj_node
);
450 attr
= new_attribute(obj_node
, "poly_points");
452 for (i
=0;i
<poly
->numpoints
;i
++) {
453 data_add_point(attr
, &poly
->points
[i
]);
458 polyconn_load(PolyConn
*poly
, ObjectNode obj_node
) /* NOTE: Does object_init() */
464 DiaObject
*obj
= &poly
->object
;
466 object_load(obj
, obj_node
);
468 attr
= object_find_attribute(obj_node
, "poly_points");
471 poly
->numpoints
= attribute_num_data(attr
);
475 object_init(obj
, poly
->numpoints
, 0);
477 data
= attribute_first_data(attr
);
478 poly
->points
= g_malloc(poly
->numpoints
*sizeof(Point
));
479 for (i
=0;i
<poly
->numpoints
;i
++) {
480 data_point(data
, &poly
->points
[i
]);
481 data
= data_next(data
);
484 obj
->handles
[0] = g_malloc(sizeof(Handle
));
485 obj
->handles
[0]->connect_type
= HANDLE_CONNECTABLE
;
486 obj
->handles
[0]->connected_to
= NULL
;
487 obj
->handles
[0]->type
= HANDLE_MAJOR_CONTROL
;
488 obj
->handles
[0]->id
= HANDLE_MOVE_STARTPOINT
;
490 obj
->handles
[poly
->numpoints
-1] = g_malloc(sizeof(Handle
));
491 obj
->handles
[poly
->numpoints
-1]->connect_type
= HANDLE_CONNECTABLE
;
492 obj
->handles
[poly
->numpoints
-1]->connected_to
= NULL
;
493 obj
->handles
[poly
->numpoints
-1]->type
= HANDLE_MAJOR_CONTROL
;
494 obj
->handles
[poly
->numpoints
-1]->id
= HANDLE_MOVE_ENDPOINT
;
496 for (i
=1;i
<poly
->numpoints
-1;i
++) {
497 obj
->handles
[i
] = g_malloc(sizeof(Handle
));
498 setup_handle(obj
->handles
[i
], PC_HANDLE_CORNER
);
501 polyconn_update_data(poly
);
505 polyconn_change_free(struct PointChange
*change
)
507 if ( (change
->type
==TYPE_ADD_POINT
&& !change
->applied
) ||
508 (change
->type
==TYPE_REMOVE_POINT
&& change
->applied
) ){
510 g_free(change
->handle
);
511 change
->handle
= NULL
;
516 polyconn_change_apply(struct PointChange
*change
, DiaObject
*obj
)
519 switch (change
->type
) {
521 add_handle((PolyConn
*)obj
, change
->pos
, &change
->point
,
524 case TYPE_REMOVE_POINT
:
525 object_unconnect(obj
, change
->handle
);
526 remove_handle((PolyConn
*)obj
, change
->pos
);
532 polyconn_change_revert(struct PointChange
*change
, DiaObject
*obj
)
534 switch (change
->type
) {
536 remove_handle((PolyConn
*)obj
, change
->pos
);
538 case TYPE_REMOVE_POINT
:
539 add_handle((PolyConn
*)obj
, change
->pos
, &change
->point
,
541 if (change
->connected_to
) {
542 object_connect(obj
, change
->handle
, change
->connected_to
);
550 static ObjectChange
*
551 polyconn_create_change(PolyConn
*poly
, enum change_type type
,
552 Point
*point
, int pos
, Handle
*handle
,
553 ConnectionPoint
*connected_to
)
555 struct PointChange
*change
;
557 change
= g_new(struct PointChange
, 1);
559 change
->obj_change
.apply
= (ObjectChangeApplyFunc
) polyconn_change_apply
;
560 change
->obj_change
.revert
= (ObjectChangeRevertFunc
) polyconn_change_revert
;
561 change
->obj_change
.free
= (ObjectChangeFreeFunc
) polyconn_change_free
;
565 change
->point
= *point
;
567 change
->handle
= handle
;
568 change
->connected_to
= connected_to
;
570 return (ObjectChange
*)change
;