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