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