1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * Flowchart toolbox -- objects for drawing flowcharts.
5 * Copyright (C) 1999 James Henstridge.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 /* DO NOT USE THIS OBJECT AS A BASIS FOR A NEW OBJECT. */
34 #include "connectionpoint.h"
35 #include "diarenderer.h"
36 #include "attributes.h"
40 #include "properties.h"
42 #include "pixmaps/diamond.xpm"
44 #define DEFAULT_WIDTH 2.0
45 #define DEFAULT_HEIGHT 1.0
46 #define DEFAULT_BORDER 0.25
48 #define NUM_CONNECTIONS 17
50 /* used when resizing to decide which side of the shape to expand/shrink */
57 typedef struct _Diamond Diamond
;
62 ConnectionPoint connections
[NUM_CONNECTIONS
];
66 gboolean show_background
;
75 typedef struct _DiamondProperties
{
76 gboolean show_background
;
81 static DiamondProperties default_properties
;
83 static real
diamond_distance_from(Diamond
*diamond
, Point
*point
);
84 static void diamond_select(Diamond
*diamond
, Point
*clicked_point
,
85 DiaRenderer
*interactive_renderer
);
86 static ObjectChange
* diamond_move_handle(Diamond
*diamond
, Handle
*handle
,
87 Point
*to
, ConnectionPoint
*cp
,
88 HandleMoveReason reason
,
89 ModifierKeys modifiers
);
90 static ObjectChange
* diamond_move(Diamond
*diamond
, Point
*to
);
91 static void diamond_draw(Diamond
*diamond
, DiaRenderer
*renderer
);
92 static void diamond_update_data(Diamond
*diamond
, AnchorShape h
,AnchorShape v
);
93 static DiaObject
*diamond_create(Point
*startpoint
,
97 static void diamond_destroy(Diamond
*diamond
);
99 static PropDescription
*diamond_describe_props(Diamond
*diamond
);
100 static void diamond_get_props(Diamond
*diamond
, GPtrArray
*props
);
101 static void diamond_set_props(Diamond
*diamond
, GPtrArray
*props
);
103 static void diamond_save(Diamond
*diamond
, ObjectNode obj_node
, const char *filename
);
104 static DiaObject
*diamond_load(ObjectNode obj_node
, int version
, const char *filename
);
106 static ObjectTypeOps diamond_type_ops
=
108 (CreateFunc
) diamond_create
,
109 (LoadFunc
) diamond_load
,
110 (SaveFunc
) diamond_save
,
111 (GetDefaultsFunc
) NULL
,
112 (ApplyDefaultsFunc
) NULL
115 DiaObjectType diamond_type
=
117 "Flowchart - Diamond", /* name */
119 (char **) diamond_xpm
, /* pixmap */
121 &diamond_type_ops
/* ops */
124 static ObjectOps diamond_ops
= {
125 (DestroyFunc
) diamond_destroy
,
126 (DrawFunc
) diamond_draw
,
127 (DistanceFunc
) diamond_distance_from
,
128 (SelectFunc
) diamond_select
,
129 (CopyFunc
) object_copy_using_properties
,
130 (MoveFunc
) diamond_move
,
131 (MoveHandleFunc
) diamond_move_handle
,
132 (GetPropertiesFunc
) object_create_props_dialog
,
133 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
134 (ObjectMenuFunc
) NULL
,
135 (DescribePropsFunc
) diamond_describe_props
,
136 (GetPropsFunc
) diamond_get_props
,
137 (SetPropsFunc
) diamond_set_props
,
140 static PropNumData text_padding_data
= { 0.0, 10.0, 0.1 };
142 static PropDescription diamond_props
[] = {
143 ELEMENT_COMMON_PROPERTIES
,
145 PROP_STD_LINE_COLOUR
,
146 PROP_STD_FILL_COLOUR
,
147 PROP_STD_SHOW_BACKGROUND
,
149 { "padding", PROP_TYPE_REAL
, PROP_FLAG_VISIBLE
,
150 N_("Text padding"), NULL
, &text_padding_data
},
152 PROP_STD_TEXT_HEIGHT
,
153 PROP_STD_TEXT_COLOUR
,
154 PROP_STD_TEXT_ALIGNMENT
,
157 { NULL
, 0, 0, NULL
, NULL
, NULL
, 0}
160 static PropDescription
*
161 diamond_describe_props(Diamond
*diamond
)
163 if (diamond_props
[0].quark
== 0)
164 prop_desc_list_calculate_quarks(diamond_props
);
165 return diamond_props
;
168 static PropOffset diamond_offsets
[] = {
169 ELEMENT_COMMON_PROPERTIES_OFFSETS
,
170 { "line_width", PROP_TYPE_REAL
, offsetof(Diamond
, border_width
) },
171 { "line_colour", PROP_TYPE_COLOUR
, offsetof(Diamond
, border_color
) },
172 { "fill_colour", PROP_TYPE_COLOUR
, offsetof(Diamond
, inner_color
) },
173 { "show_background", PROP_TYPE_BOOL
, offsetof(Diamond
, show_background
) },
174 { "line_style", PROP_TYPE_LINESTYLE
,
175 offsetof(Diamond
, line_style
), offsetof(Diamond
, dashlength
) },
176 { "padding", PROP_TYPE_REAL
, offsetof(Diamond
, padding
) },
177 {"text",PROP_TYPE_TEXT
,offsetof(Diamond
,text
)},
178 {"text_font",PROP_TYPE_FONT
,offsetof(Diamond
,attrs
.font
)},
179 {"text_height",PROP_TYPE_REAL
,offsetof(Diamond
,attrs
.height
)},
180 {"text_colour",PROP_TYPE_COLOUR
,offsetof(Diamond
,attrs
.color
)},
181 {"text_alignment",PROP_TYPE_ENUM
,offsetof(Diamond
,attrs
.alignment
)},
186 diamond_get_props(Diamond
*diamond
, GPtrArray
*props
)
188 text_get_attributes(diamond
->text
,&diamond
->attrs
);
189 object_get_props_from_offsets(&diamond
->element
.object
,
190 diamond_offsets
,props
);
194 diamond_set_props(Diamond
*diamond
, GPtrArray
*props
)
196 object_set_props_from_offsets(&diamond
->element
.object
,
197 diamond_offsets
,props
);
198 apply_textattr_properties(props
,diamond
->text
,"text",&diamond
->attrs
);
199 diamond_update_data(diamond
,ANCHOR_MIDDLE
,ANCHOR_MIDDLE
);
203 init_default_values() {
204 static int defaults_initialized
= 0;
206 if (!defaults_initialized
) {
207 default_properties
.show_background
= 1;
208 default_properties
.padding
= 0.5 * M_SQRT1_2
;
209 defaults_initialized
= 1;
214 diamond_distance_from(Diamond
*diamond
, Point
*point
)
216 Element
*elem
= &diamond
->element
;
219 rect
.left
= elem
->corner
.x
- diamond
->border_width
/2;
220 rect
.right
= elem
->corner
.x
+ elem
->width
+ diamond
->border_width
/2;
221 rect
.top
= elem
->corner
.y
- diamond
->border_width
/2;
222 rect
.bottom
= elem
->corner
.y
+ elem
->height
+ diamond
->border_width
/2;
224 if (rect
.top
> point
->y
)
225 return rect
.top
- point
->y
+
226 fabs(point
->x
- elem
->corner
.x
+ elem
->width
/ 2.0);
227 else if (point
->y
> rect
.bottom
)
228 return point
->y
- rect
.bottom
+
229 fabs(point
->x
- elem
->corner
.x
+ elem
->width
/ 2.0);
230 else if (rect
.left
> point
->x
)
231 return rect
.left
- point
->x
+
232 fabs(point
->y
- elem
->corner
.y
+ elem
->height
/ 2.0);
233 else if (point
->x
> rect
.right
)
234 return point
->x
- rect
.right
+
235 fabs(point
->y
- elem
->corner
.y
+ elem
->height
/ 2.0);
237 /* inside the bounding box of diamond ... this is where it gets harder */
238 real x
= point
->x
, y
= point
->y
;
241 /* reflect point into upper left quadrant of diamond */
242 if (x
> elem
->corner
.x
+ elem
->width
/ 2.0)
243 x
= 2 * (elem
->corner
.x
+ elem
->width
/ 2.0) - x
;
244 if (y
> elem
->corner
.y
+ elem
->height
/ 2.0)
245 y
= 2 * (elem
->corner
.y
+ elem
->height
/ 2.0) - y
;
247 dx
= -x
+ elem
->corner
.x
+ elem
->width
/ 2.0 -
248 elem
->width
/elem
->height
* (y
-elem
->corner
.y
) - diamond
->border_width
/2;
249 dy
= -y
+ elem
->corner
.y
+ elem
->height
/ 2.0 -
250 elem
->height
/elem
->width
* (x
-elem
->corner
.x
) - diamond
->border_width
/2;
251 if (dx
<= 0 || dy
<= 0)
258 diamond_select(Diamond
*diamond
, Point
*clicked_point
,
259 DiaRenderer
*interactive_renderer
)
261 text_set_cursor(diamond
->text
, clicked_point
, interactive_renderer
);
262 text_grab_focus(diamond
->text
, &diamond
->element
.object
);
264 element_update_handles(&diamond
->element
);
268 diamond_move_handle(Diamond
*diamond
, Handle
*handle
,
269 Point
*to
, ConnectionPoint
*cp
,
270 HandleMoveReason reason
, ModifierKeys modifiers
)
272 AnchorShape horiz
= ANCHOR_MIDDLE
, vert
= ANCHOR_MIDDLE
;
274 assert(diamond
!=NULL
);
275 assert(handle
!=NULL
);
278 element_move_handle(&diamond
->element
, handle
->id
, to
, cp
,
281 switch (handle
->id
) {
282 case HANDLE_RESIZE_NW
:
283 horiz
= ANCHOR_END
; vert
= ANCHOR_END
; break;
284 case HANDLE_RESIZE_N
:
285 vert
= ANCHOR_END
; break;
286 case HANDLE_RESIZE_NE
:
287 horiz
= ANCHOR_START
; vert
= ANCHOR_END
; break;
288 case HANDLE_RESIZE_E
:
289 horiz
= ANCHOR_START
; break;
290 case HANDLE_RESIZE_SE
:
291 horiz
= ANCHOR_START
; vert
= ANCHOR_START
; break;
292 case HANDLE_RESIZE_S
:
293 vert
= ANCHOR_START
; break;
294 case HANDLE_RESIZE_SW
:
295 horiz
= ANCHOR_END
; vert
= ANCHOR_START
; break;
296 case HANDLE_RESIZE_W
:
297 horiz
= ANCHOR_END
; break;
301 diamond_update_data(diamond
, horiz
, vert
);
307 diamond_move(Diamond
*diamond
, Point
*to
)
309 diamond
->element
.corner
= *to
;
311 diamond_update_data(diamond
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
317 diamond_draw(Diamond
*diamond
, DiaRenderer
*renderer
)
319 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
323 assert(diamond
!= NULL
);
324 assert(renderer
!= NULL
);
326 elem
= &diamond
->element
;
328 pts
[0] = pts
[1] = pts
[2] = pts
[3] = elem
->corner
;
329 pts
[0].x
+= elem
->width
/ 2.0;
330 pts
[1].x
+= elem
->width
;
331 pts
[1].y
+= elem
->height
/ 2.0;
332 pts
[2].x
+= elem
->width
/ 2.0;
333 pts
[2].y
+= elem
->height
;
334 pts
[3].y
+= elem
->height
/ 2.0;
336 if (diamond
->show_background
) {
337 renderer_ops
->set_fillstyle(renderer
, FILLSTYLE_SOLID
);
339 renderer_ops
->fill_polygon(renderer
,
341 &diamond
->inner_color
);
344 renderer_ops
->set_linewidth(renderer
, diamond
->border_width
);
345 renderer_ops
->set_linestyle(renderer
, diamond
->line_style
);
346 renderer_ops
->set_dashlength(renderer
, diamond
->dashlength
);
347 renderer_ops
->set_linejoin(renderer
, LINEJOIN_MITER
);
349 renderer_ops
->draw_polygon(renderer
,
351 &diamond
->border_color
);
353 text_draw(diamond
->text
, renderer
);
358 diamond_update_data(Diamond
*diamond
, AnchorShape horiz
, AnchorShape vert
)
360 Element
*elem
= &diamond
->element
;
361 DiaObject
*obj
= &elem
->object
;
362 ElementBBExtras
*extra
= &elem
->extra_spacing
;
363 Point center
, bottom_right
;
368 /* save starting points */
369 center
= bottom_right
= elem
->corner
;
370 center
.x
+= elem
->width
/2;
371 bottom_right
.x
+= elem
->width
;
372 center
.y
+= elem
->height
/2;
373 bottom_right
.y
+= elem
->height
;
375 text_calc_boundingbox(diamond
->text
, NULL
);
376 width
= diamond
->text
->max_width
+ 2*diamond
->padding
+diamond
->border_width
;
377 height
= diamond
->text
->height
* diamond
->text
->numlines
+
378 2 * diamond
->padding
+ diamond
->border_width
;
380 if (height
> (elem
->width
- width
) * elem
->height
/ elem
->width
) {
381 /* increase size of the diamond while keeping its aspect ratio */
382 real grad
= elem
->width
/elem
->height
;
383 if (grad
< 1.0/4) grad
= 1.0/4;
384 if (grad
> 4) grad
= 4;
385 elem
->width
= width
+ height
* grad
;
386 elem
->height
= height
+ width
/ grad
;
388 /* Need this for text alignment soon */
389 real grad
= elem
->width
/elem
->height
;
390 if (grad
< 1.0/4) grad
= 1.0/4;
391 if (grad
> 4) grad
= 4;
392 width
= elem
->width
- height
*grad
;
395 /* move shape if necessary ... */
398 elem
->corner
.x
= center
.x
- elem
->width
/2; break;
400 elem
->corner
.x
= bottom_right
.x
- elem
->width
; break;
406 elem
->corner
.y
= center
.y
- elem
->height
/2; break;
408 elem
->corner
.y
= bottom_right
.y
- elem
->height
; break;
414 p
.x
+= elem
->width
/ 2.0;
415 p
.y
+= elem
->height
/ 2.0 - diamond
->text
->height
*diamond
->text
->numlines
/2 +
416 diamond
->text
->ascent
;
417 switch (diamond
->text
->alignment
) {
427 text_set_position(diamond
->text
, &p
);
429 dw
= elem
->width
/ 8.0;
430 dh
= elem
->height
/ 8.0;
431 /* Update connections: */
432 diamond
->connections
[0].pos
.x
= elem
->corner
.x
+ 4*dw
;
433 diamond
->connections
[0].pos
.y
= elem
->corner
.y
;
434 diamond
->connections
[1].pos
.x
= elem
->corner
.x
+ 5*dw
;
435 diamond
->connections
[1].pos
.y
= elem
->corner
.y
+ dh
;
436 diamond
->connections
[2].pos
.x
= elem
->corner
.x
+ 6*dw
;
437 diamond
->connections
[2].pos
.y
= elem
->corner
.y
+ 2*dh
;
438 diamond
->connections
[3].pos
.x
= elem
->corner
.x
+ 7*dw
;
439 diamond
->connections
[3].pos
.y
= elem
->corner
.y
+ 3*dh
;
440 diamond
->connections
[4].pos
.x
= elem
->corner
.x
+ elem
->width
;
441 diamond
->connections
[4].pos
.y
= elem
->corner
.y
+ 4*dh
;
442 diamond
->connections
[5].pos
.x
= elem
->corner
.x
+ 7*dw
;
443 diamond
->connections
[5].pos
.y
= elem
->corner
.y
+ 5*dh
;
444 diamond
->connections
[6].pos
.x
= elem
->corner
.x
+ 6*dw
;
445 diamond
->connections
[6].pos
.y
= elem
->corner
.y
+ 6*dh
;
446 diamond
->connections
[7].pos
.x
= elem
->corner
.x
+ 5*dw
;
447 diamond
->connections
[7].pos
.y
= elem
->corner
.y
+ 7*dh
;
448 diamond
->connections
[8].pos
.x
= elem
->corner
.x
+ 4*dw
;
449 diamond
->connections
[8].pos
.y
= elem
->corner
.y
+ elem
->height
;
450 diamond
->connections
[9].pos
.x
= elem
->corner
.x
+ 3*dw
;
451 diamond
->connections
[9].pos
.y
= elem
->corner
.y
+ 7*dh
;
452 diamond
->connections
[10].pos
.x
= elem
->corner
.x
+ 2*dw
;
453 diamond
->connections
[10].pos
.y
= elem
->corner
.y
+ 6*dh
;
454 diamond
->connections
[11].pos
.x
= elem
->corner
.x
+ dw
;
455 diamond
->connections
[11].pos
.y
= elem
->corner
.y
+ 5*dh
;
456 diamond
->connections
[12].pos
.x
= elem
->corner
.x
;
457 diamond
->connections
[12].pos
.y
= elem
->corner
.y
+ 4*dh
;
458 diamond
->connections
[13].pos
.x
= elem
->corner
.x
+ dw
;
459 diamond
->connections
[13].pos
.y
= elem
->corner
.y
+ 3*dh
;
460 diamond
->connections
[14].pos
.x
= elem
->corner
.x
+ 2*dw
;
461 diamond
->connections
[14].pos
.y
= elem
->corner
.y
+ 2*dh
;
462 diamond
->connections
[15].pos
.x
= elem
->corner
.x
+ 3*dw
;
463 diamond
->connections
[15].pos
.y
= elem
->corner
.y
+ dh
;
464 diamond
->connections
[16].pos
.x
= elem
->corner
.x
+ 4*dw
;
465 diamond
->connections
[16].pos
.y
= elem
->corner
.y
+ 4*dh
;
467 extra
->border_trans
= diamond
->border_width
/ 2.0;
468 element_update_boundingbox(elem
);
470 obj
->position
= elem
->corner
;
472 element_update_handles(elem
);
476 diamond_create(Point
*startpoint
,
486 DiaFont
*font
= NULL
;
489 init_default_values();
491 diamond
= g_malloc0(sizeof(Diamond
));
492 elem
= &diamond
->element
;
495 obj
->type
= &diamond_type
;
497 obj
->ops
= &diamond_ops
;
499 elem
->corner
= *startpoint
;
500 elem
->width
= DEFAULT_WIDTH
;
501 elem
->height
= DEFAULT_HEIGHT
;
503 diamond
->border_width
= attributes_get_default_linewidth();
504 diamond
->border_color
= attributes_get_foreground();
505 diamond
->inner_color
= attributes_get_background();
506 diamond
->show_background
= default_properties
.show_background
;
507 attributes_get_default_line_style(&diamond
->line_style
,&diamond
->dashlength
);
509 diamond
->padding
= default_properties
.padding
;
511 attributes_get_default_font(&font
, &font_height
);
513 p
.x
+= elem
->width
/ 2.0;
514 p
.y
+= elem
->height
/ 2.0 + font_height
/ 2;
515 diamond
->text
= new_text("", font
, font_height
, &p
, &diamond
->border_color
,
517 text_get_attributes(diamond
->text
,&diamond
->attrs
);
518 dia_font_unref(font
);
520 element_init(elem
, 8, NUM_CONNECTIONS
);
522 for (i
=0;i
<NUM_CONNECTIONS
;i
++) {
523 obj
->connections
[i
] = &diamond
->connections
[i
];
524 diamond
->connections
[i
].object
= obj
;
525 diamond
->connections
[i
].connected
= NULL
;
526 diamond
->connections
[i
].flags
= 0;
528 diamond
->connections
[16].flags
= CP_FLAGS_MAIN
;
530 diamond_update_data(diamond
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
533 *handle2
= obj
->handles
[7];
534 return &diamond
->element
.object
;
538 diamond_destroy(Diamond
*diamond
)
540 text_destroy(diamond
->text
);
542 element_destroy(&diamond
->element
);
547 diamond_save(Diamond
*diamond
, ObjectNode obj_node
, const char *filename
)
549 element_save(&diamond
->element
, obj_node
);
551 if (diamond
->border_width
!= 0.1)
552 data_add_real(new_attribute(obj_node
, "border_width"),
553 diamond
->border_width
);
555 if (!color_equals(&diamond
->border_color
, &color_black
))
556 data_add_color(new_attribute(obj_node
, "border_color"),
557 &diamond
->border_color
);
559 if (!color_equals(&diamond
->inner_color
, &color_white
))
560 data_add_color(new_attribute(obj_node
, "inner_color"),
561 &diamond
->inner_color
);
563 data_add_boolean(new_attribute(obj_node
, "show_background"), diamond
->show_background
);
565 if (diamond
->line_style
!= LINESTYLE_SOLID
)
566 data_add_enum(new_attribute(obj_node
, "line_style"),
567 diamond
->line_style
);
569 if (diamond
->line_style
!= LINESTYLE_SOLID
&&
570 diamond
->dashlength
!= DEFAULT_LINESTYLE_DASHLEN
)
571 data_add_real(new_attribute(obj_node
, "dashlength"),
572 diamond
->dashlength
);
574 data_add_real(new_attribute(obj_node
, "padding"), diamond
->padding
);
576 data_add_text(new_attribute(obj_node
, "text"), diamond
->text
);
580 diamond_load(ObjectNode obj_node
, int version
, const char *filename
)
588 diamond
= g_malloc0(sizeof(Diamond
));
589 elem
= &diamond
->element
;
592 obj
->type
= &diamond_type
;
593 obj
->ops
= &diamond_ops
;
595 element_load(elem
, obj_node
);
597 diamond
->border_width
= 0.1;
598 attr
= object_find_attribute(obj_node
, "border_width");
600 diamond
->border_width
= data_real( attribute_first_data(attr
) );
602 diamond
->border_color
= color_black
;
603 attr
= object_find_attribute(obj_node
, "border_color");
605 data_color(attribute_first_data(attr
), &diamond
->border_color
);
607 diamond
->inner_color
= color_white
;
608 attr
= object_find_attribute(obj_node
, "inner_color");
610 data_color(attribute_first_data(attr
), &diamond
->inner_color
);
612 diamond
->show_background
= TRUE
;
613 attr
= object_find_attribute(obj_node
, "show_background");
615 diamond
->show_background
= data_boolean( attribute_first_data(attr
) );
617 diamond
->line_style
= LINESTYLE_SOLID
;
618 attr
= object_find_attribute(obj_node
, "line_style");
620 diamond
->line_style
= data_enum( attribute_first_data(attr
) );
622 diamond
->dashlength
= DEFAULT_LINESTYLE_DASHLEN
;
623 attr
= object_find_attribute(obj_node
, "dashlength");
625 diamond
->dashlength
= data_real(attribute_first_data(attr
));
627 diamond
->padding
= default_properties
.padding
;
628 attr
= object_find_attribute(obj_node
, "padding");
630 diamond
->padding
= data_real( attribute_first_data(attr
) );
632 diamond
->text
= NULL
;
633 attr
= object_find_attribute(obj_node
, "text");
635 diamond
->text
= data_text(attribute_first_data(attr
));
637 element_init(elem
, 8, NUM_CONNECTIONS
);
639 for (i
=0;i
<NUM_CONNECTIONS
;i
++) {
640 obj
->connections
[i
] = &diamond
->connections
[i
];
641 diamond
->connections
[i
].object
= obj
;
642 diamond
->connections
[i
].connected
= NULL
;
643 diamond
->connections
[i
].flags
= 0;
645 diamond
->connections
[16].flags
= CP_FLAGS_MAIN
;
647 diamond_update_data(diamond
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
649 return &diamond
->element
.object
;