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 
36 #include "tool-icons.h"
37 
38 #define DEFAULT_WIDTH 2.0
39 #define DEFAULT_HEIGHT 1.0
40 #define DEFAULT_BORDER 0.25
41 
42 #define NUM_CONNECTIONS 9
43 
44 typedef enum {
45   FREE_ASPECT,
46   FIXED_ASPECT,
47   SQUARE_ASPECT
48 } AspectType;
49 
50 typedef struct _Box Box;
51 
52 struct _Box {
53   Element element;
54 
55   ConnectionPoint connections[NUM_CONNECTIONS];
56 
57   real border_width;
58   Color border_color;
59   Color inner_color;
60   gboolean show_background;
61   LineStyle line_style;
62   real dashlength;
63   real corner_radius;
64   AspectType aspect;
65 };
66 
67 static struct _BoxProperties {
68   gboolean show_background;
69   real corner_radius;
70   AspectType aspect;
71 } default_properties = { TRUE, 0.0 };
72 
73 static real box_distance_from(Box *box, Point *point);
74 static void box_select(Box *box, Point *clicked_point,
75 		       DiaRenderer *interactive_renderer);
76 static ObjectChange* box_move_handle(Box *box, Handle *handle,
77 			    Point *to, ConnectionPoint *cp,
78 				     HandleMoveReason reason,
79 			    ModifierKeys modifiers);
80 static ObjectChange* box_move(Box *box, Point *to);
81 static void box_draw(Box *box, DiaRenderer *renderer);
82 static void box_update_data(Box *box);
83 static DiaObject *box_create(Point *startpoint,
84 			  void *user_data,
85 			  Handle **handle1,
86 			  Handle **handle2);
87 static void box_destroy(Box *box);
88 static DiaObject *box_copy(Box *box);
89 
90 static PropDescription *box_describe_props(Box *box);
91 static void box_get_props(Box *box, GPtrArray *props);
92 static void box_set_props(Box *box, GPtrArray *props);
93 
94 static void box_save(Box *box, ObjectNode obj_node, const char *filename);
95 static DiaObject *box_load(ObjectNode obj_node, int version, const char *filename);
96 static DiaMenu *box_get_object_menu(Box *box, Point *clickedpoint);
97 
98 static ObjectTypeOps box_type_ops =
99 {
100   (CreateFunc) box_create,
101   (LoadFunc)   box_load,
102   (SaveFunc)   box_save,
103   (GetDefaultsFunc)   NULL,
104   (ApplyDefaultsFunc) NULL
105 };
106 
107 DiaObjectType box_type =
108 {
109   "Standard - Box",  /* name */
110   0,                 /* version */
111   (char **) box_icon, /* pixmap */
112 
113   &box_type_ops      /* ops */
114 };
115 
116 DiaObjectType *_box_type = (DiaObjectType *) &box_type;
117 
118 static ObjectOps box_ops = {
119   (DestroyFunc)         box_destroy,
120   (DrawFunc)            box_draw,
121   (DistanceFunc)        box_distance_from,
122   (SelectFunc)          box_select,
123   (CopyFunc)            box_copy,
124   (MoveFunc)            box_move,
125   (MoveHandleFunc)      box_move_handle,
126   (GetPropertiesFunc)   object_create_props_dialog,
127   (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
128   (ObjectMenuFunc)      box_get_object_menu,
129   (DescribePropsFunc)   box_describe_props,
130   (GetPropsFunc)        box_get_props,
131   (SetPropsFunc)        box_set_props,
132   (TextEditFunc) 0,
133   (ApplyPropertiesListFunc) object_apply_props,
134 };
135 
136 static PropNumData corner_radius_data = { 0.0, 10.0, 0.1 };
137 
138 static PropEnumData prop_aspect_data[] = {
139   { N_("Free"), FREE_ASPECT },
140   { N_("Fixed"), FIXED_ASPECT },
141   { N_("Square"), SQUARE_ASPECT },
142   { NULL, 0 }
143 };
144 static PropDescription box_props[] = {
145   ELEMENT_COMMON_PROPERTIES,
146   PROP_STD_LINE_WIDTH,
147   PROP_STD_LINE_COLOUR,
148   PROP_STD_FILL_COLOUR,
149   PROP_STD_SHOW_BACKGROUND,
150   PROP_STD_LINE_STYLE,
151   { "corner_radius", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
152     N_("Corner radius"), NULL, &corner_radius_data },
153   { "aspect", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
154     N_("Aspect ratio"), NULL, prop_aspect_data },
155   PROP_DESC_END
156 };
157 
158 static PropDescription *
box_describe_props(Box * box)159 box_describe_props(Box *box)
160 {
161   if (box_props[0].quark == 0)
162     prop_desc_list_calculate_quarks(box_props);
163   return box_props;
164 }
165 
166 static PropOffset box_offsets[] = {
167   ELEMENT_COMMON_PROPERTIES_OFFSETS,
168   { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(Box, border_width) },
169   { "line_colour", PROP_TYPE_COLOUR, offsetof(Box, border_color) },
170   { "fill_colour", PROP_TYPE_COLOUR, offsetof(Box, inner_color) },
171   { "show_background", PROP_TYPE_BOOL, offsetof(Box, show_background) },
172   { "aspect", PROP_TYPE_ENUM, offsetof(Box, aspect) },
173   { "line_style", PROP_TYPE_LINESTYLE,
174     offsetof(Box, line_style), offsetof(Box, dashlength) },
175   { "corner_radius", PROP_TYPE_REAL, offsetof(Box, corner_radius) },
176   { NULL, 0, 0 }
177 };
178 
179 static void
box_get_props(Box * box,GPtrArray * props)180 box_get_props(Box *box, GPtrArray *props)
181 {
182   object_get_props_from_offsets(&box->element.object,
183                                 box_offsets, props);
184 }
185 
186 static void
box_set_props(Box * box,GPtrArray * props)187 box_set_props(Box *box, GPtrArray *props)
188 {
189   object_set_props_from_offsets(&box->element.object,
190                                 box_offsets, props);
191   box_update_data(box);
192 }
193 
194 static real
box_distance_from(Box * box,Point * point)195 box_distance_from(Box *box, Point *point)
196 {
197   Element *elem = &box->element;
198   Rectangle rect;
199 
200   rect.left = elem->corner.x - box->border_width/2;
201   rect.right = elem->corner.x + elem->width + box->border_width/2;
202   rect.top = elem->corner.y - box->border_width/2;
203   rect.bottom = elem->corner.y + elem->height + box->border_width/2;
204   return distance_rectangle_point(&rect, point);
205 }
206 
207 static void
box_select(Box * box,Point * clicked_point,DiaRenderer * interactive_renderer)208 box_select(Box *box, Point *clicked_point,
209 	   DiaRenderer *interactive_renderer)
210 {
211   real radius;
212 
213   element_update_handles(&box->element);
214 
215   if (box->corner_radius > 0) {
216     Element *elem = (Element *)box;
217     radius = box->corner_radius;
218     radius = MIN(radius, elem->width/2);
219     radius = MIN(radius, elem->height/2);
220     radius *= (1-M_SQRT1_2);
221 
222     elem->resize_handles[0].pos.x += radius;
223     elem->resize_handles[0].pos.y += radius;
224     elem->resize_handles[2].pos.x -= radius;
225     elem->resize_handles[2].pos.y += radius;
226     elem->resize_handles[5].pos.x += radius;
227     elem->resize_handles[5].pos.y -= radius;
228     elem->resize_handles[7].pos.x -= radius;
229     elem->resize_handles[7].pos.y -= radius;
230   }
231 }
232 
233 static ObjectChange*
box_move_handle(Box * box,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)234 box_move_handle(Box *box, Handle *handle,
235 		Point *to, ConnectionPoint *cp,
236 		HandleMoveReason reason, ModifierKeys modifiers)
237 {
238   assert(box!=NULL);
239   assert(handle!=NULL);
240   assert(to!=NULL);
241 
242   if (box->aspect != FREE_ASPECT){
243     double width, height;
244     double new_width, new_height;
245     double to_width, aspect_width;
246     Point corner = box->element.corner;
247     Point se_to;
248 
249     width = box->element.width;
250     height = box->element.height;
251     switch (handle->id) {
252     case HANDLE_RESIZE_N:
253     case HANDLE_RESIZE_S:
254       new_height = fabs(to->y - corner.y);
255       new_width = new_height / height * width;
256       break;
257     case HANDLE_RESIZE_W:
258     case HANDLE_RESIZE_E:
259       new_width = fabs(to->x - corner.x);
260       new_height = new_width / width * height;
261       break;
262     case HANDLE_RESIZE_NW:
263     case HANDLE_RESIZE_NE:
264     case HANDLE_RESIZE_SW:
265     case HANDLE_RESIZE_SE:
266       to_width = fabs(to->x - corner.x);
267       aspect_width = fabs(to->y - corner.y) / height * width;
268       new_width = to_width > aspect_width ? to_width : aspect_width;
269       new_height = new_width / width * height;
270       break;
271     default:
272       new_width = width;
273       new_height = height;
274       break;
275     }
276 
277     se_to.x = corner.x + new_width;
278     se_to.y = corner.y + new_height;
279 
280     element_move_handle(&box->element, HANDLE_RESIZE_SE, &se_to, cp, reason, modifiers);
281   } else {
282     element_move_handle(&box->element, handle->id, to, cp, reason, modifiers);
283   }
284 
285   box_update_data(box);
286 
287   return NULL;
288 }
289 
290 static ObjectChange*
box_move(Box * box,Point * to)291 box_move(Box *box, Point *to)
292 {
293   box->element.corner = *to;
294 
295   box_update_data(box);
296 
297   return NULL;
298 }
299 
300 static void
box_draw(Box * box,DiaRenderer * renderer)301 box_draw(Box *box, DiaRenderer *renderer)
302 {
303   Point lr_corner;
304   Element *elem;
305   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
306 
307 
308   assert(box != NULL);
309   assert(renderer != NULL);
310 
311   elem = &box->element;
312 
313   lr_corner.x = elem->corner.x + elem->width;
314   lr_corner.y = elem->corner.y + elem->height;
315 
316   renderer_ops->set_linewidth(renderer, box->border_width);
317   renderer_ops->set_linestyle(renderer, box->line_style);
318   renderer_ops->set_dashlength(renderer, box->dashlength);
319   if (box->corner_radius > 0)
320     renderer_ops->set_linejoin(renderer, LINEJOIN_ROUND);
321   else
322     renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
323 
324   if (box->show_background) {
325     renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
326 
327     /* Problem:  How do we make the fill with rounded corners? */
328     if (box->corner_radius > 0) {
329       renderer_ops->fill_rounded_rect(renderer,
330 				       &elem->corner,
331 				       &lr_corner,
332 				       &box->inner_color,
333 				       box->corner_radius);
334     } else {
335       renderer_ops->fill_rect(renderer,
336 			       &elem->corner,
337 			       &lr_corner,
338 			       &box->inner_color);
339     }
340   }
341 
342   if (box->corner_radius > 0) {
343     renderer_ops->draw_rounded_rect(renderer,
344 			     &elem->corner,
345 			     &lr_corner,
346 			     &box->border_color,
347 			     box->corner_radius);
348   } else {
349     renderer_ops->draw_rect(renderer,
350 			     &elem->corner,
351 			     &lr_corner,
352 			     &box->border_color);
353   }
354 }
355 
356 
357 static void
box_update_data(Box * box)358 box_update_data(Box *box)
359 {
360   Element *elem = &box->element;
361   ElementBBExtras *extra = &elem->extra_spacing;
362   DiaObject *obj = &elem->object;
363   real radius;
364 
365   if (box->aspect == SQUARE_ASPECT){
366     float size = elem->height < elem->width ? elem->height : elem->width;
367     elem->height = elem->width = size;
368   }
369 
370   radius = box->corner_radius;
371   radius = MIN(radius, elem->width/2);
372   radius = MIN(radius, elem->height/2);
373   radius *= (1-M_SQRT1_2);
374 
375   /* Update connections: */
376   box->connections[0].pos.x = elem->corner.x + radius;
377   box->connections[0].pos.y = elem->corner.y + radius;
378   box->connections[1].pos.x = elem->corner.x + elem->width / 2.0;
379   box->connections[1].pos.y = elem->corner.y;
380   box->connections[2].pos.x = elem->corner.x + elem->width - radius;
381   box->connections[2].pos.y = elem->corner.y + radius;
382   box->connections[3].pos.x = elem->corner.x;
383   box->connections[3].pos.y = elem->corner.y + elem->height / 2.0;
384   box->connections[4].pos.x = elem->corner.x + elem->width;
385   box->connections[4].pos.y = elem->corner.y + elem->height / 2.0;
386   box->connections[5].pos.x = elem->corner.x + radius;
387   box->connections[5].pos.y = elem->corner.y + elem->height - radius;
388   box->connections[6].pos.x = elem->corner.x + elem->width / 2.0;
389   box->connections[6].pos.y = elem->corner.y + elem->height;
390   box->connections[7].pos.x = elem->corner.x + elem->width - radius;
391   box->connections[7].pos.y = elem->corner.y + elem->height - radius;
392   box->connections[8].pos.x = elem->corner.x + elem->width / 2.0;
393   box->connections[8].pos.y = elem->corner.y + elem->height / 2.0;
394 
395   box->connections[0].directions = DIR_NORTH|DIR_WEST;
396   box->connections[1].directions = DIR_NORTH;
397   box->connections[2].directions = DIR_NORTH|DIR_EAST;
398   box->connections[3].directions = DIR_WEST;
399   box->connections[4].directions = DIR_EAST;
400   box->connections[5].directions = DIR_SOUTH|DIR_WEST;
401   box->connections[6].directions = DIR_SOUTH;
402   box->connections[7].directions = DIR_SOUTH|DIR_EAST;
403   box->connections[8].directions = DIR_ALL;
404 
405   extra->border_trans = box->border_width / 2.0;
406   element_update_boundingbox(elem);
407 
408   obj->position = elem->corner;
409 
410   element_update_handles(elem);
411 
412   if (radius > 0.0) {
413     /* Fix the handles, too */
414     elem->resize_handles[0].pos.x += radius;
415     elem->resize_handles[0].pos.y += radius;
416     elem->resize_handles[2].pos.x -= radius;
417     elem->resize_handles[2].pos.y += radius;
418     elem->resize_handles[5].pos.x += radius;
419     elem->resize_handles[5].pos.y -= radius;
420     elem->resize_handles[7].pos.x -= radius;
421     elem->resize_handles[7].pos.y -= radius;
422   }
423 }
424 
425 static DiaObject *
box_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)426 box_create(Point *startpoint,
427 	   void *user_data,
428 	   Handle **handle1,
429 	   Handle **handle2)
430 {
431   Box *box;
432   Element *elem;
433   DiaObject *obj;
434   int i;
435 
436   box = g_malloc0(sizeof(Box));
437   elem = &box->element;
438   obj = &elem->object;
439 
440   obj->type = &box_type;
441 
442   obj->ops = &box_ops;
443 
444   elem->corner = *startpoint;
445   elem->width = DEFAULT_WIDTH;
446   elem->height = DEFAULT_HEIGHT;
447 
448   box->border_width =  attributes_get_default_linewidth();
449   box->border_color = attributes_get_foreground();
450   box->inner_color = attributes_get_background();
451   attributes_get_default_line_style(&box->line_style, &box->dashlength);
452   /* For non-default objects, this is overridden by the default */
453   box->show_background = default_properties.show_background;
454   box->corner_radius = default_properties.corner_radius;
455   box->aspect = default_properties.aspect;
456 
457   element_init(elem, 8, NUM_CONNECTIONS);
458 
459   for (i=0;i<NUM_CONNECTIONS;i++) {
460     obj->connections[i] = &box->connections[i];
461     box->connections[i].object = obj;
462     box->connections[i].connected = NULL;
463   }
464   box->connections[8].flags = CP_FLAGS_MAIN;
465 
466   box_update_data(box);
467 
468   *handle1 = NULL;
469   *handle2 = obj->handles[7];
470   return &box->element.object;
471 }
472 
473 static void
box_destroy(Box * box)474 box_destroy(Box *box)
475 {
476   element_destroy(&box->element);
477 }
478 
479 static DiaObject *
box_copy(Box * box)480 box_copy(Box *box)
481 {
482   int i;
483   Box *newbox;
484   Element *elem, *newelem;
485   DiaObject *newobj;
486 
487   elem = &box->element;
488 
489   newbox = g_malloc0(sizeof(Box));
490   newelem = &newbox->element;
491   newobj = &newelem->object;
492 
493   element_copy(elem, newelem);
494 
495   newbox->border_width = box->border_width;
496   newbox->border_color = box->border_color;
497   newbox->inner_color = box->inner_color;
498   newbox->show_background = box->show_background;
499   newbox->line_style = box->line_style;
500   newbox->dashlength = box->dashlength;
501   newbox->corner_radius = box->corner_radius;
502   newbox->aspect = box->aspect;
503 
504   for (i=0;i<NUM_CONNECTIONS;i++) {
505     newobj->connections[i] = &newbox->connections[i];
506     newbox->connections[i].object = newobj;
507     newbox->connections[i].connected = NULL;
508     newbox->connections[i].pos = box->connections[i].pos;
509     newbox->connections[i].last_pos = box->connections[i].last_pos;
510     newbox->connections[i].flags = box->connections[i].flags;
511   }
512 
513   return &newbox->element.object;
514 }
515 
516 static void
box_save(Box * box,ObjectNode obj_node,const char * filename)517 box_save(Box *box, ObjectNode obj_node, const char *filename)
518 {
519   element_save(&box->element, obj_node);
520 
521   if (box->border_width != 0.1)
522     data_add_real(new_attribute(obj_node, "border_width"),
523 		  box->border_width);
524 
525   if (!color_equals(&box->border_color, &color_black))
526     data_add_color(new_attribute(obj_node, "border_color"),
527 		   &box->border_color);
528 
529   if (!color_equals(&box->inner_color, &color_white))
530     data_add_color(new_attribute(obj_node, "inner_color"),
531 		   &box->inner_color);
532 
533   data_add_boolean(new_attribute(obj_node, "show_background"), box->show_background);
534 
535   if (box->line_style != LINESTYLE_SOLID)
536     data_add_enum(new_attribute(obj_node, "line_style"),
537 		  box->line_style);
538 
539   if (box->line_style != LINESTYLE_SOLID &&
540       box->dashlength != DEFAULT_LINESTYLE_DASHLEN)
541     data_add_real(new_attribute(obj_node, "dashlength"),
542                   box->dashlength);
543 
544   if (box->corner_radius > 0.0)
545     data_add_real(new_attribute(obj_node, "corner_radius"),
546 		  box->corner_radius);
547 
548   if (box->aspect != FREE_ASPECT)
549     data_add_enum(new_attribute(obj_node, "aspect"),
550 		  box->aspect);
551 }
552 
553 static DiaObject *
box_load(ObjectNode obj_node,int version,const char * filename)554 box_load(ObjectNode obj_node, int version, const char *filename)
555 {
556   Box *box;
557   Element *elem;
558   DiaObject *obj;
559   int i;
560   AttributeNode attr;
561 
562   box = g_malloc0(sizeof(Box));
563   elem = &box->element;
564   obj = &elem->object;
565 
566   obj->type = &box_type;
567   obj->ops = &box_ops;
568 
569   element_load(elem, obj_node);
570 
571   box->border_width = 0.1;
572   attr = object_find_attribute(obj_node, "border_width");
573   if (attr != NULL)
574     box->border_width =  data_real( attribute_first_data(attr) );
575 
576   box->border_color = color_black;
577   attr = object_find_attribute(obj_node, "border_color");
578   if (attr != NULL)
579     data_color(attribute_first_data(attr), &box->border_color);
580 
581   box->inner_color = color_white;
582   attr = object_find_attribute(obj_node, "inner_color");
583   if (attr != NULL)
584     data_color(attribute_first_data(attr), &box->inner_color);
585 
586   box->show_background = TRUE;
587   attr = object_find_attribute(obj_node, "show_background");
588   if (attr != NULL)
589     box->show_background = data_boolean( attribute_first_data(attr) );
590 
591   box->line_style = LINESTYLE_SOLID;
592   attr = object_find_attribute(obj_node, "line_style");
593   if (attr != NULL)
594     box->line_style =  data_enum( attribute_first_data(attr) );
595 
596   box->dashlength = DEFAULT_LINESTYLE_DASHLEN;
597   attr = object_find_attribute(obj_node, "dashlength");
598   if (attr != NULL)
599     box->dashlength = data_real(attribute_first_data(attr));
600 
601   box->corner_radius = 0.0;
602   attr = object_find_attribute(obj_node, "corner_radius");
603   if (attr != NULL)
604     box->corner_radius =  data_real( attribute_first_data(attr) );
605 
606   box->aspect = FREE_ASPECT;
607   attr = object_find_attribute(obj_node, "aspect");
608   if (attr != NULL)
609     box->aspect = data_enum(attribute_first_data(attr));
610 
611   element_init(elem, 8, NUM_CONNECTIONS);
612 
613   for (i=0;i<NUM_CONNECTIONS;i++) {
614     obj->connections[i] = &box->connections[i];
615     box->connections[i].object = obj;
616     box->connections[i].connected = NULL;
617   }
618   box->connections[8].flags = CP_FLAGS_MAIN;
619 
620   box_update_data(box);
621 
622   return &box->element.object;
623 }
624 
625 
626 struct AspectChange {
627   ObjectChange obj_change;
628   AspectType old_type, new_type;
629   /* The points before this got applied.  Afterwards, all points can be
630    * calculated.
631    */
632   Point topleft;
633   real width, height;
634 };
635 
636 static void
aspect_change_free(struct AspectChange * change)637 aspect_change_free(struct AspectChange *change)
638 {
639 }
640 
641 static void
aspect_change_apply(struct AspectChange * change,DiaObject * obj)642 aspect_change_apply(struct AspectChange *change, DiaObject *obj)
643 {
644   Box *box = (Box*)obj;
645 
646   box->aspect = change->new_type;
647   box_update_data(box);
648 }
649 
650 static void
aspect_change_revert(struct AspectChange * change,DiaObject * obj)651 aspect_change_revert(struct AspectChange *change, DiaObject *obj)
652 {
653   Box *box = (Box*)obj;
654 
655   box->aspect = change->old_type;
656   box->element.corner = change->topleft;
657   box->element.width = change->width;
658   box->element.height = change->height;
659   box_update_data(box);
660 }
661 
662 static ObjectChange *
aspect_create_change(Box * box,AspectType aspect)663 aspect_create_change(Box *box, AspectType aspect)
664 {
665   struct AspectChange *change;
666 
667   change = g_new0(struct AspectChange, 1);
668 
669   change->obj_change.apply = (ObjectChangeApplyFunc) aspect_change_apply;
670   change->obj_change.revert = (ObjectChangeRevertFunc) aspect_change_revert;
671   change->obj_change.free = (ObjectChangeFreeFunc) aspect_change_free;
672 
673   change->old_type = box->aspect;
674   change->new_type = aspect;
675   change->topleft = box->element.corner;
676   change->width = box->element.width;
677   change->height = box->element.height;
678 
679   return (ObjectChange *)change;
680 }
681 
682 
683 static ObjectChange *
box_set_aspect_callback(DiaObject * obj,Point * clicked,gpointer data)684 box_set_aspect_callback (DiaObject* obj, Point* clicked, gpointer data)
685 {
686   ObjectChange *change;
687 
688   change = aspect_create_change((Box*)obj, (AspectType)data);
689   change->apply(change, obj);
690 
691   return change;
692 }
693 
694 static DiaMenuItem box_menu_items[] = {
695   { N_("Free aspect"), box_set_aspect_callback, (void*)FREE_ASPECT,
696     DIAMENU_ACTIVE|DIAMENU_TOGGLE },
697   { N_("Fixed aspect"), box_set_aspect_callback, (void*)FIXED_ASPECT,
698     DIAMENU_ACTIVE|DIAMENU_TOGGLE },
699   { N_("Square"), box_set_aspect_callback, (void*)SQUARE_ASPECT,
700     DIAMENU_ACTIVE|DIAMENU_TOGGLE},
701 };
702 
703 static DiaMenu box_menu = {
704   "Box",
705   sizeof(box_menu_items)/sizeof(DiaMenuItem),
706   box_menu_items,
707   NULL
708 };
709 
710 static DiaMenu *
box_get_object_menu(Box * box,Point * clickedpoint)711 box_get_object_menu(Box *box, Point *clickedpoint)
712 {
713   /* Set entries sensitive/selected etc here */
714   box_menu_items[0].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE;
715   box_menu_items[1].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE;
716   box_menu_items[2].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE;
717 
718   box_menu_items[box->aspect].active =
719     DIAMENU_ACTIVE|DIAMENU_TOGGLE|DIAMENU_TOGGLE_ON;
720 
721   return &box_menu;
722 }
723