1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 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.
19 /** The Element object type is a rectangular box that has
20 * non-connectable handles on its corners and on the midsts of the
21 * edges. It also has connectionpoints in the same places as the
22 * handles as well as a mainpoint in the middle.
30 #include <string.h> /* memcpy() */
35 /** Update the boundingbox information for this element.
36 * @param An object to update bounding box on.
39 element_update_boundingbox(Element
*elem
) {
42 ElementBBExtras
*extra
= &elem
->extra_spacing
;
46 corner
= &elem
->corner
;
48 bb
.right
= corner
->x
+ elem
->width
;
50 bb
.bottom
= corner
->y
+ elem
->height
;
52 rectangle_bbox(&bb
,extra
,&elem
->object
.bounding_box
);
55 /** Update the 9 connections of this element to form a rectangle and
56 * point in the center.
57 * The connections go left-to-right, first top row, then middle row, then
58 * bottom row, then center. Do not blindly use this in old objects where
59 * the order is different, as it will mess with the saved files. If an
60 * object uses element_update_handles, it can use this.
61 * @param elem The element to update
62 * @param cps The list of connectionpoints to update, in the order described.
63 * Usually, this will be the same list as elem->connectionpoints.
66 element_update_connections_rectangle(Element
*elem
,
69 cps
[0].pos
= elem
->corner
;
70 cps
[1].pos
.x
= elem
->corner
.x
+ elem
->width
/ 2.0;
71 cps
[1].pos
.y
= elem
->corner
.y
;
72 cps
[2].pos
.x
= elem
->corner
.x
+ elem
->width
;
73 cps
[2].pos
.y
= elem
->corner
.y
;
74 cps
[3].pos
.x
= elem
->corner
.x
;
75 cps
[3].pos
.y
= elem
->corner
.y
+ elem
->height
/ 2.0;
76 cps
[4].pos
.x
= elem
->corner
.x
+ elem
->width
;
77 cps
[4].pos
.y
= elem
->corner
.y
+ elem
->height
/ 2.0;
78 cps
[5].pos
.x
= elem
->corner
.x
;
79 cps
[5].pos
.y
= elem
->corner
.y
+ elem
->height
;
80 cps
[6].pos
.x
= elem
->corner
.x
+ elem
->width
/ 2.0;
81 cps
[6].pos
.y
= elem
->corner
.y
+ elem
->height
;
82 cps
[7].pos
.x
= elem
->corner
.x
+ elem
->width
;
83 cps
[7].pos
.y
= elem
->corner
.y
+ elem
->height
;
84 g_assert(elem
->object
.num_connections
>= 9);
85 cps
[8].pos
.x
= elem
->corner
.x
+ elem
->width
/ 2.0;
86 cps
[8].pos
.y
= elem
->corner
.y
+ elem
->height
/ 2.0;
88 cps
[0].directions
= DIR_NORTH
|DIR_WEST
;
89 cps
[1].directions
= DIR_NORTH
;
90 cps
[2].directions
= DIR_NORTH
|DIR_EAST
;
91 cps
[3].directions
= DIR_WEST
;
92 cps
[4].directions
= DIR_EAST
;
93 cps
[5].directions
= DIR_SOUTH
|DIR_WEST
;
94 cps
[6].directions
= DIR_SOUTH
;
95 cps
[7].directions
= DIR_SOUTH
|DIR_EAST
;
96 cps
[8].directions
= DIR_ALL
;
99 /** Update the corner and edge handles of an element to reflect its position
101 * @param elem An element to update.
104 element_update_handles(Element
*elem
)
106 Point
*corner
= &elem
->corner
;
108 elem
->resize_handles
[0].id
= HANDLE_RESIZE_NW
;
109 elem
->resize_handles
[0].pos
.x
= corner
->x
;
110 elem
->resize_handles
[0].pos
.y
= corner
->y
;
112 elem
->resize_handles
[1].id
= HANDLE_RESIZE_N
;
113 elem
->resize_handles
[1].pos
.x
= corner
->x
+ elem
->width
/2.0;
114 elem
->resize_handles
[1].pos
.y
= corner
->y
;
116 elem
->resize_handles
[2].id
= HANDLE_RESIZE_NE
;
117 elem
->resize_handles
[2].pos
.x
= corner
->x
+ elem
->width
;
118 elem
->resize_handles
[2].pos
.y
= corner
->y
;
120 elem
->resize_handles
[3].id
= HANDLE_RESIZE_W
;
121 elem
->resize_handles
[3].pos
.x
= corner
->x
;
122 elem
->resize_handles
[3].pos
.y
= corner
->y
+ elem
->height
/2.0;
124 elem
->resize_handles
[4].id
= HANDLE_RESIZE_E
;
125 elem
->resize_handles
[4].pos
.x
= corner
->x
+ elem
->width
;
126 elem
->resize_handles
[4].pos
.y
= corner
->y
+ elem
->height
/2.0;
128 elem
->resize_handles
[5].id
= HANDLE_RESIZE_SW
;
129 elem
->resize_handles
[5].pos
.x
= corner
->x
;
130 elem
->resize_handles
[5].pos
.y
= corner
->y
+ elem
->height
;
132 elem
->resize_handles
[6].id
= HANDLE_RESIZE_S
;
133 elem
->resize_handles
[6].pos
.x
= corner
->x
+ elem
->width
/2.0;
134 elem
->resize_handles
[6].pos
.y
= corner
->y
+ elem
->height
;
136 elem
->resize_handles
[7].id
= HANDLE_RESIZE_SE
;
137 elem
->resize_handles
[7].pos
.x
= corner
->x
+ elem
->width
;
138 elem
->resize_handles
[7].pos
.y
= corner
->y
+ elem
->height
;
141 /** Handle the moving of one of the elements handles.
142 * This function is suitable for use as the move_handle object operation.
143 * @param elem The element whose handle is being moved.
144 * @param id The id of the handle.
145 * @param to Where it's being moved to.
147 * @param reason What is causing this handle to be moved (creation, movement..)
148 * @param modifiers Any modifier keys (shift, control...) that the user is
150 * @return Undo information for this change.
153 element_move_handle(Element
*elem
, HandleId id
,
154 Point
*to
, ConnectionPoint
*cp
,
155 HandleMoveReason reason
, ModifierKeys modifiers
)
160 assert(id
>=HANDLE_RESIZE_NW
);
161 assert(id
<=HANDLE_RESIZE_SE
);
163 corner
= &elem
->corner
;
166 point_sub(&p
, &elem
->corner
);
169 case HANDLE_RESIZE_NW
:
170 if ( to
->x
< (corner
->x
+elem
->width
)) {
174 if ( to
->y
< (corner
->y
+elem
->height
)) {
179 case HANDLE_RESIZE_N
:
180 if ( to
->y
< (corner
->y
+elem
->height
)) {
185 case HANDLE_RESIZE_NE
:
188 if ( to
->y
< (corner
->y
+elem
->height
)) {
193 case HANDLE_RESIZE_W
:
194 if ( to
->x
< (corner
->x
+elem
->width
)) {
199 case HANDLE_RESIZE_E
:
203 case HANDLE_RESIZE_SW
:
204 if ( to
->x
< (corner
->x
+elem
->width
)) {
211 case HANDLE_RESIZE_S
:
215 case HANDLE_RESIZE_SE
:
222 message_error("Error, called element_move_handle() with wrong handle-id\n");
227 /** Move the handle of an element restricted to a certain aspect ration.
228 * @param elem The element to update on
229 * @param id The id of the handle being moved
230 * @param to Where the handle is being moved to
231 * @param aspect_ratio The aspect ratio (width:height) to obey.
232 * The aspect ratio must not be 0.
235 element_move_handle_aspect(Element
*elem
, HandleId id
,
236 Point
*to
, real aspect_ratio
)
241 real new_width
, new_height
;
244 assert(id
>=HANDLE_RESIZE_NW
);
245 assert(id
<=HANDLE_RESIZE_SE
);
247 corner
= &elem
->corner
;
250 point_sub(&p
, &elem
->corner
);
253 height
= elem
->height
;
260 case HANDLE_RESIZE_NW
:
261 new_width
= width
- p
.x
;
262 new_height
= height
- p
.y
;
266 case HANDLE_RESIZE_N
:
267 new_height
= height
- p
.y
;
271 case HANDLE_RESIZE_NE
:
273 new_height
= height
- p
.y
;
277 case HANDLE_RESIZE_W
:
278 new_width
= width
- p
.x
;
282 case HANDLE_RESIZE_E
:
287 case HANDLE_RESIZE_SW
:
288 new_width
= width
- p
.x
;
293 case HANDLE_RESIZE_S
:
298 case HANDLE_RESIZE_SE
:
305 message_error("Error, called element_move_handle() with wrong handle-id\n");
308 /* Which of the two versions to use: */
309 if (new_width
> new_height
*aspect_ratio
) {
310 new_height
= new_width
/aspect_ratio
;
312 new_width
= new_height
*aspect_ratio
;
315 if ( (new_width
<0.0) || (new_height
<0.0)) {
320 corner
->x
-= (new_width
- width
)*move_x
;
321 corner
->y
-= (new_height
- height
)*move_y
;
323 elem
->width
= new_width
;
324 elem
->height
= new_height
;
327 /** Initialization function for element objects.
328 * An element must have at least 8 handles and 9 connectionpoints.
329 * @param elem The element to initialize. This function will call
330 * object_init() on the element.
331 * @param num_handles The number of handles to set up (>= 8). The handles
332 * will be initialized by this function.
333 * @param num_connections The number of connectionpoints to set up (>= 9).
334 * The connectionpoints will <em>not</em> be
335 * initialized by this function.
338 element_init(Element
*elem
, int num_handles
, int num_connections
)
345 assert(num_handles
>=8);
347 object_init(obj
, num_handles
, num_connections
);
350 obj
->handles
[i
] = &elem
->resize_handles
[i
];
351 obj
->handles
[i
]->connect_type
= HANDLE_NONCONNECTABLE
;
352 obj
->handles
[i
]->connected_to
= NULL
;
353 obj
->handles
[i
]->type
= HANDLE_MAJOR_CONTROL
;
357 /** Copy an element, initializing the handles.
358 * This function will in turn copy the underlying object.
359 * @paramm from An element to copy from.
360 * @param to An element (already allocated) to copy to.
363 element_copy(Element
*from
, Element
*to
)
365 DiaObject
*toobj
, *fromobj
;
368 fromobj
= &from
->object
;
371 object_copy(fromobj
, toobj
);
373 to
->corner
= from
->corner
;
374 to
->width
= from
->width
;
375 to
->height
= from
->height
;
378 to
->resize_handles
[i
] = from
->resize_handles
[i
];
379 to
->resize_handles
[i
].connected_to
= NULL
;
380 toobj
->handles
[i
] = &to
->resize_handles
[i
];
382 memcpy(&to
->extra_spacing
,&from
->extra_spacing
,sizeof(to
->extra_spacing
));
385 /** Destroy an elements private information.
386 * This function will in turn call object_destroy.
387 * @param elem The element to destroy. It will <em>not</em> be deallocated
388 * by this call, but will not be valid afterwards.
391 element_destroy(Element
*elem
)
393 object_destroy(&elem
->object
);
396 /** Save the element-specific parts of this element to XML.
401 element_save(Element
*elem
, ObjectNode obj_node
)
403 object_save(&elem
->object
, obj_node
);
405 data_add_point(new_attribute(obj_node
, "elem_corner"),
407 data_add_real(new_attribute(obj_node
, "elem_width"),
409 data_add_real(new_attribute(obj_node
, "elem_height"),
413 void element_load(Element
*elem
, ObjectNode obj_node
)
417 object_load(&elem
->object
, obj_node
);
419 elem
->corner
.x
= 0.0;
420 elem
->corner
.y
= 0.0;
421 attr
= object_find_attribute(obj_node
, "elem_corner");
423 data_point( attribute_first_data(attr
), &elem
->corner
);
426 attr
= object_find_attribute(obj_node
, "elem_width");
428 elem
->width
= data_real( attribute_first_data(attr
));
431 attr
= object_find_attribute(obj_node
, "elem_height");
433 elem
->height
= data_real( attribute_first_data(attr
));