1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 *
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.
8 *
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.
13 *
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.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <assert.h>
24 #include <math.h>
25
26 #include "intl.h"
27 #include "object.h"
28 #include "element.h"
29 #include "connectionpoint.h"
30 #include "diarenderer.h"
31 #include "attributes.h"
32 #include "widgets.h"
33 #include "message.h"
34 #include "properties.h"
35 #include "diagramdata.h"
36 #include "parent.h"
37 #include "newgroup.h"
38
39 #include "../objects/Misc/pixmaps/newgroup.xpm"
40
41 #define NUM_CONNECTIONS 9
42
43 #define DEFAULT_WIDTH 2
44 #define DEFAULT_HEIGHT 2
45
46 typedef struct _Group NewGroup;
47
48 struct _Group {
49 Element element;
50
51 gboolean is_open;
52 ConnectionPoint connections[NUM_CONNECTIONS];
53 };
54
55 static real newgroup_distance_from(NewGroup *group, Point *point);
56 static void newgroup_select(NewGroup *group, Point *clicked_point,
57 DiaRenderer *interactive_renderer);
58 static ObjectChange* newgroup_move_handle(NewGroup *group, Handle *handle,
59 Point *to, ConnectionPoint *cp,
60 HandleMoveReason reason,
61 ModifierKeys modifiers);
62 static ObjectChange* newgroup_move(NewGroup *group, Point *to);
63 static void newgroup_draw(NewGroup *group, DiaRenderer *renderer);
64 static void newgroup_update_data(NewGroup *group);
65 static DiaObject *newgroup_create(Point *startpoint,
66 void *user_data,
67 Handle **handle1,
68 Handle **handle2);
69 static void newgroup_destroy(NewGroup *group);
70 static DiaObject *newgroup_copy(NewGroup *group);
71
72 static PropDescription *newgroup_describe_props(NewGroup *group);
73 static void newgroup_get_props(NewGroup *group, GPtrArray *props);
74 static void newgroup_set_props(NewGroup *group, GPtrArray *props);
75
76 static void newgroup_save(NewGroup *group, ObjectNode obj_node, const char *filename);
77 static DiaObject *newgroup_load(ObjectNode obj_node, int version, const char *filename);
78
79 static ObjectTypeOps newgroup_type_ops =
80 {
81 (CreateFunc) newgroup_create,
82 (LoadFunc) newgroup_load,
83 (SaveFunc) newgroup_save,
84 (GetDefaultsFunc) NULL,
85 (ApplyDefaultsFunc) NULL
86 };
87
88 DiaObjectType newgroup_type =
89 {
90 "Misc - NewGroup", /* name */
91 0, /* version */
92 (char **) newgroup_icon, /* pixmap */
93
94 &newgroup_type_ops /* ops */
95 };
96
97 DiaObjectType *_newgroup_type = (DiaObjectType *) &newgroup_type;
98
99 static ObjectOps newgroup_ops = {
100 (DestroyFunc) newgroup_destroy,
101 (DrawFunc) newgroup_draw,
102 (DistanceFunc) newgroup_distance_from,
103 (SelectFunc) newgroup_select,
104 (CopyFunc) newgroup_copy,
105 (MoveFunc) newgroup_move,
106 (MoveHandleFunc) newgroup_move_handle,
107 (GetPropertiesFunc) object_create_props_dialog,
108 (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
109 (ObjectMenuFunc) NULL,
110 (DescribePropsFunc) newgroup_describe_props,
111 (GetPropsFunc) newgroup_get_props,
112 (SetPropsFunc) newgroup_set_props,
113 (TextEditFunc) 0,
114 #ifdef __GNUC__
115 #warning NewGroup requires a function in the vtable to apply props
116 #else
117 #pragma message("warning: NewGroup requires a function in the vtable to apply props")
118 #endif
119 };
120
121 static PropDescription newgroup_props[] = {
122 ELEMENT_COMMON_PROPERTIES,
123 { "open", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
124 N_("Open group"), NULL, NULL},
125
126 PROP_DESC_END
127 };
128
129 static PropDescription *
newgroup_describe_props(NewGroup * group)130 newgroup_describe_props(NewGroup *group)
131 {
132 if (newgroup_props[0].quark == 0)
133 prop_desc_list_calculate_quarks(newgroup_props);
134 return newgroup_props;
135 }
136
137 static PropOffset newgroup_offsets[] = {
138 ELEMENT_COMMON_PROPERTIES_OFFSETS,
139 { "open", PROP_TYPE_BOOL, offsetof(NewGroup, is_open) },
140 { NULL, 0, 0 }
141 };
142
143 static void
newgroup_get_props(NewGroup * group,GPtrArray * props)144 newgroup_get_props(NewGroup *group, GPtrArray *props)
145 {
146 object_get_props_from_offsets(&group->element.object,
147 newgroup_offsets, props);
148 }
149
150 static void
newgroup_set_props(NewGroup * group,GPtrArray * props)151 newgroup_set_props(NewGroup *group, GPtrArray *props)
152 {
153 object_set_props_from_offsets(&group->element.object,
154 newgroup_offsets, props);
155 newgroup_update_data(group);
156 }
157
158 static real
newgroup_distance_from(NewGroup * group,Point * point)159 newgroup_distance_from(NewGroup *group, Point *point)
160 {
161 Element *elem = &group->element;
162 Rectangle rect;
163
164 rect.left = elem->corner.x;
165 rect.right = elem->corner.x + elem->width;
166 rect.top = elem->corner.y;
167 rect.bottom = elem->corner.y + elem->height;
168 return distance_rectangle_point(&rect, point);
169 }
170
171 static void
newgroup_select(NewGroup * group,Point * clicked_point,DiaRenderer * interactive_renderer)172 newgroup_select(NewGroup *group, Point *clicked_point,
173 DiaRenderer *interactive_renderer)
174 {
175 element_update_handles(&group->element);
176 }
177
178 static ObjectChange*
newgroup_move_handle(NewGroup * group,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)179 newgroup_move_handle(NewGroup *group, Handle *handle,
180 Point *to, ConnectionPoint *cp,
181 HandleMoveReason reason, ModifierKeys modifiers)
182 {
183 assert(group!=NULL);
184 assert(handle!=NULL);
185 assert(to!=NULL);
186
187 element_move_handle(&group->element, handle->id, to, cp, reason, modifiers);
188
189 newgroup_update_data(group);
190
191 return NULL;
192 }
193
194 static ObjectChange*
newgroup_move(NewGroup * group,Point * to)195 newgroup_move(NewGroup *group, Point *to)
196 {
197 group->element.corner = *to;
198
199 newgroup_update_data(group);
200
201 return NULL;
202 }
203
204 static void
newgroup_draw(NewGroup * group,DiaRenderer * renderer)205 newgroup_draw(NewGroup *group, DiaRenderer *renderer)
206 {
207 Point lr_corner;
208 Element *elem;
209 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
210
211
212 assert(group != NULL);
213 assert(renderer != NULL);
214
215 elem = &group->element;
216
217 lr_corner.x = elem->corner.x + elem->width;
218 lr_corner.y = elem->corner.y + elem->height;
219
220 renderer_ops->set_linewidth(renderer, 0.01);
221 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
222 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
223
224 renderer_ops->draw_rect(renderer,
225 &elem->corner,
226 &lr_corner,
227 &color_black);
228 }
229
230 static void
newgroup_update_data(NewGroup * group)231 newgroup_update_data(NewGroup *group)
232 {
233 Element *elem = &group->element;
234 /* ElementBBExtras *extra = &elem->extra_spacing; */
235 DiaObject *obj = &elem->object;
236
237 /* Update connections: */
238 group->connections[0].pos.x = elem->corner.x;
239 group->connections[0].pos.y = elem->corner.y;
240 group->connections[1].pos.x = elem->corner.x + elem->width / 2.0;
241 group->connections[1].pos.y = elem->corner.y;
242 group->connections[2].pos.x = elem->corner.x + elem->width;
243 group->connections[2].pos.y = elem->corner.y;
244 group->connections[3].pos.x = elem->corner.x;
245 group->connections[3].pos.y = elem->corner.y + elem->height / 2.0;
246 group->connections[4].pos.x = elem->corner.x + elem->width;
247 group->connections[4].pos.y = elem->corner.y + elem->height / 2.0;
248 group->connections[5].pos.x = elem->corner.x;
249 group->connections[5].pos.y = elem->corner.y + elem->height;
250 group->connections[6].pos.x = elem->corner.x + elem->width / 2.0;
251 group->connections[6].pos.y = elem->corner.y + elem->height;
252 group->connections[7].pos.x = elem->corner.x + elem->width;
253 group->connections[7].pos.y = elem->corner.y + elem->height;
254 group->connections[8].pos.x = elem->corner.x + elem->width / 2.0;
255 group->connections[8].pos.y = elem->corner.y + elem->height / 2.0;
256
257 group->connections[0].directions = DIR_NORTH|DIR_WEST;
258 group->connections[1].directions = DIR_NORTH;
259 group->connections[2].directions = DIR_NORTH|DIR_EAST;
260 group->connections[3].directions = DIR_WEST;
261 group->connections[4].directions = DIR_EAST;
262 group->connections[5].directions = DIR_SOUTH|DIR_WEST;
263 group->connections[6].directions = DIR_SOUTH;
264 group->connections[7].directions = DIR_SOUTH|DIR_EAST;
265 group->connections[8].directions = DIR_ALL;
266
267 element_update_boundingbox(elem);
268
269 obj->position = elem->corner;
270
271 element_update_handles(elem);
272
273 if (group->is_open) {
274 obj->flags &= ~DIA_OBJECT_GRABS_CHILD_INPUT;
275 } else {
276 gboolean newlySet = FALSE;
277 Layer *layer;
278 if (!object_flags_set(obj, DIA_OBJECT_GRABS_CHILD_INPUT)) {
279 newlySet = TRUE;
280 }
281 obj->flags |= DIA_OBJECT_GRABS_CHILD_INPUT;
282 if (newlySet) {
283 layer = dia_object_get_parent_layer(obj);
284 if (layer != NULL) { /* Placed in diagram already */
285 GList *children = g_list_prepend(NULL, obj);
286 children = parent_list_affected(children);
287 /* Remove the group object that stayed at the start of the list,
288 leaving only the children */
289 children = g_list_remove_link(children, children);
290 #if 0 /* this introduces a crircular dependency, does not work on win32 and is bad style everywhere */
291 diagram_unselect_objects(layer_get_parent_diagram(layer), children);
292 #else
293 g_warning ("used to call diagram_unselect_objects()");
294 #endif
295 g_list_free(children);
296 }
297 }
298 }
299 }
300
301 static DiaObject *
newgroup_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)302 newgroup_create(Point *startpoint,
303 void *user_data,
304 Handle **handle1,
305 Handle **handle2)
306 {
307 NewGroup *group;
308 Element *elem;
309 DiaObject *obj;
310 int i;
311
312 group = g_malloc0(sizeof(NewGroup));
313 elem = &group->element;
314 obj = &elem->object;
315
316 obj->type = &newgroup_type;
317
318 obj->ops = &newgroup_ops;
319 obj->flags |= DIA_OBJECT_CAN_PARENT|DIA_OBJECT_GRABS_CHILD_INPUT;
320
321 elem->corner = *startpoint;
322 elem->width = DEFAULT_WIDTH;
323 elem->height = DEFAULT_HEIGHT;
324
325 element_init(elem, 8, NUM_CONNECTIONS);
326
327 for (i=0;i<NUM_CONNECTIONS;i++) {
328 obj->connections[i] = &group->connections[i];
329 group->connections[i].object = obj;
330 group->connections[i].connected = NULL;
331 }
332 group->connections[8].flags = CP_FLAGS_MAIN;
333
334 newgroup_update_data(group);
335
336 if (handle1 != NULL) {
337 *handle1 = NULL;
338 }
339 if (handle2 != NULL) {
340 *handle2 = obj->handles[7];
341 }
342 return &group->element.object;
343 }
344
345 static void
newgroup_destroy(NewGroup * group)346 newgroup_destroy(NewGroup *group)
347 {
348 element_destroy(&group->element);
349 }
350
351 static DiaObject *
newgroup_copy(NewGroup * group)352 newgroup_copy(NewGroup *group)
353 {
354 int i;
355 NewGroup *newgroup;
356 Element *elem, *newelem;
357 DiaObject *newobj;
358
359 elem = &group->element;
360
361 newgroup = g_malloc0(sizeof(NewGroup));
362 newelem = &newgroup->element;
363 newobj = &newelem->object;
364
365 element_copy(elem, newelem);
366
367 for (i=0;i<NUM_CONNECTIONS;i++) {
368 newobj->connections[i] = &newgroup->connections[i];
369 newgroup->connections[i].object = newobj;
370 newgroup->connections[i].connected = NULL;
371 newgroup->connections[i].pos = group->connections[i].pos;
372 newgroup->connections[i].last_pos = group->connections[i].last_pos;
373 newgroup->connections[i].flags = group->connections[i].flags;
374 }
375
376 return &newgroup->element.object;
377 }
378
379 static void
newgroup_save(NewGroup * group,ObjectNode obj_node,const char * filename)380 newgroup_save(NewGroup *group, ObjectNode obj_node, const char *filename)
381 {
382 element_save(&group->element, obj_node);
383 }
384
385 static DiaObject *
newgroup_load(ObjectNode obj_node,int version,const char * filename)386 newgroup_load(ObjectNode obj_node, int version, const char *filename)
387 {
388 NewGroup *group;
389 Element *elem;
390 DiaObject *obj;
391 int i;
392
393 group = g_malloc0(sizeof(NewGroup));
394 elem = &group->element;
395 obj = &elem->object;
396
397 obj->type = &newgroup_type;
398 obj->ops = &newgroup_ops;
399
400 element_load(elem, obj_node);
401 element_init(elem, 8, NUM_CONNECTIONS);
402
403 for (i=0;i<NUM_CONNECTIONS;i++) {
404 obj->connections[i] = &group->connections[i];
405 group->connections[i].object = obj;
406 group->connections[i].connected = NULL;
407 }
408 group->connections[8].flags = CP_FLAGS_MAIN;
409
410 newgroup_update_data(group);
411
412 return &group->element.object;
413 }
414