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/pgram.xpm"
44 /* used when resizing to decide which side of the shape to expand/shrink */
51 #define DEFAULT_WIDTH 2.0
52 #define DEFAULT_HEIGHT 1.0
53 #define DEFAULT_BORDER 0.25
55 #define NUM_CONNECTIONS 17
57 typedef struct _Pgram Pgram
;
62 ConnectionPoint connections
[NUM_CONNECTIONS
];
66 gboolean show_background
;
69 real shear_angle
, shear_grad
;
76 typedef struct _PgramProperties
{
77 gboolean show_background
;
82 static PgramProperties default_properties
;
84 static real
pgram_distance_from(Pgram
*pgram
, Point
*point
);
85 static void pgram_select(Pgram
*pgram
, Point
*clicked_point
,
86 DiaRenderer
*interactive_renderer
);
87 static ObjectChange
* pgram_move_handle(Pgram
*pgram
, Handle
*handle
,
88 Point
*to
, ConnectionPoint
*cp
,
89 HandleMoveReason reason
,
90 ModifierKeys modifiers
);
91 static ObjectChange
* pgram_move(Pgram
*pgram
, Point
*to
);
92 static void pgram_draw(Pgram
*pgram
, DiaRenderer
*renderer
);
93 static void pgram_update_data(Pgram
*pgram
, AnchorShape h
, AnchorShape v
);
94 static DiaObject
*pgram_create(Point
*startpoint
,
98 static void pgram_destroy(Pgram
*pgram
);
100 static PropDescription
*pgram_describe_props(Pgram
*pgram
);
101 static void pgram_get_props(Pgram
*pgram
, GPtrArray
*props
);
102 static void pgram_set_props(Pgram
*pgram
, GPtrArray
*props
);
104 static void pgram_save(Pgram
*pgram
, ObjectNode obj_node
, const char *filename
);
105 static DiaObject
*pgram_load(ObjectNode obj_node
, int version
, const char *filename
);
107 static ObjectTypeOps pgram_type_ops
=
109 (CreateFunc
) pgram_create
,
110 (LoadFunc
) pgram_load
,
111 (SaveFunc
) pgram_save
,
112 (GetDefaultsFunc
) NULL
,
113 (ApplyDefaultsFunc
) NULL
116 DiaObjectType pgram_type
=
118 "Flowchart - Parallelogram", /* name */
120 (char **) pgram_xpm
, /* pixmap */
122 &pgram_type_ops
/* ops */
125 static ObjectOps pgram_ops
= {
126 (DestroyFunc
) pgram_destroy
,
127 (DrawFunc
) pgram_draw
,
128 (DistanceFunc
) pgram_distance_from
,
129 (SelectFunc
) pgram_select
,
130 (CopyFunc
) object_copy_using_properties
,
131 (MoveFunc
) pgram_move
,
132 (MoveHandleFunc
) pgram_move_handle
,
133 (GetPropertiesFunc
) object_create_props_dialog
,
134 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
135 (ObjectMenuFunc
) NULL
,
136 (DescribePropsFunc
) pgram_describe_props
,
137 (GetPropsFunc
) pgram_get_props
,
138 (SetPropsFunc
) pgram_set_props
,
141 static PropNumData text_padding_data
= { 0.0, 10.0, 0.1 };
142 static PropNumData shear_angle_data
= { 45.0, 135.0, 1.0 };
144 static PropDescription pgram_props
[] = {
145 ELEMENT_COMMON_PROPERTIES
,
147 PROP_STD_LINE_COLOUR
,
148 PROP_STD_FILL_COLOUR
,
149 PROP_STD_SHOW_BACKGROUND
,
151 { "shear_angle", PROP_TYPE_REAL
, PROP_FLAG_VISIBLE
,
152 N_("Shear angle"), NULL
, &shear_angle_data
},
153 { "padding", PROP_TYPE_REAL
, PROP_FLAG_VISIBLE
,
154 N_("Text padding"), NULL
, &text_padding_data
},
156 PROP_STD_TEXT_HEIGHT
,
157 PROP_STD_TEXT_COLOUR
,
158 PROP_STD_TEXT_ALIGNMENT
,
161 { NULL
, 0, 0, NULL
, NULL
, NULL
, 0}
164 static PropDescription
*
165 pgram_describe_props(Pgram
*pgram
)
167 if (pgram_props
[0].quark
== 0)
168 prop_desc_list_calculate_quarks(pgram_props
);
172 static PropOffset pgram_offsets
[] = {
173 ELEMENT_COMMON_PROPERTIES_OFFSETS
,
174 { "line_width", PROP_TYPE_REAL
, offsetof(Pgram
, border_width
) },
175 { "line_colour", PROP_TYPE_COLOUR
, offsetof(Pgram
, border_color
) },
176 { "fill_colour", PROP_TYPE_COLOUR
, offsetof(Pgram
, inner_color
) },
177 { "show_background", PROP_TYPE_BOOL
, offsetof(Pgram
, show_background
) },
178 { "line_style", PROP_TYPE_LINESTYLE
,
179 offsetof(Pgram
, line_style
), offsetof(Pgram
, dashlength
) },
180 { "shear_angle", PROP_TYPE_REAL
, offsetof(Pgram
, shear_angle
) },
181 { "padding", PROP_TYPE_REAL
, offsetof(Pgram
, padding
) },
182 {"text",PROP_TYPE_TEXT
,offsetof(Pgram
,text
)},
183 {"text_font",PROP_TYPE_FONT
,offsetof(Pgram
,attrs
.font
)},
184 {"text_height",PROP_TYPE_REAL
,offsetof(Pgram
,attrs
.height
)},
185 {"text_colour",PROP_TYPE_COLOUR
,offsetof(Pgram
,attrs
.color
)},
186 {"text_alignment",PROP_TYPE_ENUM
,offsetof(Pgram
,attrs
.alignment
)},
191 pgram_get_props(Pgram
*pgram
, GPtrArray
*props
)
193 text_get_attributes(pgram
->text
,&pgram
->attrs
);
194 object_get_props_from_offsets(&pgram
->element
.object
,
195 pgram_offsets
,props
);
199 pgram_set_props(Pgram
*pgram
, GPtrArray
*props
)
201 object_set_props_from_offsets(&pgram
->element
.object
,
202 pgram_offsets
,props
);
203 apply_textattr_properties(props
,pgram
->text
,"text",&pgram
->attrs
);
204 pgram_update_data(pgram
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
208 init_default_values() {
209 static int defaults_initialized
= 0;
211 if (!defaults_initialized
) {
212 default_properties
.show_background
= 1;
213 default_properties
.shear_angle
= 70.0;
214 default_properties
.padding
= 0.5;
215 defaults_initialized
= 1;
220 pgram_distance_from(Pgram
*pgram
, Point
*point
)
222 Element
*elem
= &pgram
->element
;
225 rect
.left
= elem
->corner
.x
- pgram
->border_width
/2;
226 rect
.right
= elem
->corner
.x
+ elem
->width
+ pgram
->border_width
/2;
227 rect
.top
= elem
->corner
.y
- pgram
->border_width
/2;
228 rect
.bottom
= elem
->corner
.y
+ elem
->height
+ pgram
->border_width
/2;
230 /* we do some fiddling with the left/right values to get good accuracy
231 * without having to write a new distance checking routine */
232 if (rect
.top
> point
->y
) {
233 /* point above parallelogram */
234 if (pgram
->shear_grad
> 0)
235 rect
.left
+= pgram
->shear_grad
* (rect
.bottom
- rect
.top
);
237 rect
.right
+= pgram
->shear_grad
* (rect
.bottom
- rect
.top
);
238 } else if (rect
.bottom
< point
->y
) {
239 /* point below parallelogram */
240 if (pgram
->shear_grad
> 0)
241 rect
.right
-= pgram
->shear_grad
* (rect
.bottom
- rect
.top
);
243 rect
.left
-= pgram
->shear_grad
* (rect
.bottom
- rect
.top
);
245 /* point withing vertical interval of parallelogram -- modify
246 * left and right sides to `unshear' the parallelogram. This
247 * increases accuracy for points near the */
248 if (pgram
->shear_grad
> 0) {
249 rect
.left
+= pgram
->shear_grad
* (rect
.bottom
- point
->y
);
250 rect
.right
-= pgram
->shear_grad
* (point
->y
- rect
.top
);
252 rect
.left
-= pgram
->shear_grad
* (point
->y
- rect
.top
);
253 rect
.right
+= pgram
->shear_grad
* (rect
.bottom
- point
->y
);
257 return distance_rectangle_point(&rect
, point
);
261 pgram_select(Pgram
*pgram
, Point
*clicked_point
,
262 DiaRenderer
*interactive_renderer
)
264 text_set_cursor(pgram
->text
, clicked_point
, interactive_renderer
);
265 text_grab_focus(pgram
->text
, &pgram
->element
.object
);
267 element_update_handles(&pgram
->element
);
271 pgram_move_handle(Pgram
*pgram
, Handle
*handle
,
272 Point
*to
, ConnectionPoint
*cp
,
273 HandleMoveReason reason
, ModifierKeys modifiers
)
275 AnchorShape horiz
= ANCHOR_MIDDLE
, vert
= ANCHOR_MIDDLE
;
278 assert(handle
!=NULL
);
281 element_move_handle(&pgram
->element
, handle
->id
, to
, cp
, reason
, modifiers
);
283 switch (handle
->id
) {
284 case HANDLE_RESIZE_NW
:
285 horiz
= ANCHOR_END
; vert
= ANCHOR_END
; break;
286 case HANDLE_RESIZE_N
:
287 vert
= ANCHOR_END
; break;
288 case HANDLE_RESIZE_NE
:
289 horiz
= ANCHOR_START
; vert
= ANCHOR_END
; break;
290 case HANDLE_RESIZE_E
:
291 horiz
= ANCHOR_START
; break;
292 case HANDLE_RESIZE_SE
:
293 horiz
= ANCHOR_START
; vert
= ANCHOR_START
; break;
294 case HANDLE_RESIZE_S
:
295 vert
= ANCHOR_START
; break;
296 case HANDLE_RESIZE_SW
:
297 horiz
= ANCHOR_END
; vert
= ANCHOR_START
; break;
298 case HANDLE_RESIZE_W
:
299 horiz
= ANCHOR_END
; break;
303 pgram_update_data(pgram
, horiz
, vert
);
309 pgram_move(Pgram
*pgram
, Point
*to
)
311 pgram
->element
.corner
= *to
;
313 pgram_update_data(pgram
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
319 pgram_draw(Pgram
*pgram
, DiaRenderer
*renderer
)
321 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
326 assert(pgram
!= NULL
);
327 assert(renderer
!= NULL
);
329 elem
= &pgram
->element
;
331 pts
[0] = pts
[1] = pts
[2] = pts
[3] = elem
->corner
;
332 pts
[1].x
+= elem
->width
;
333 pts
[2].x
+= elem
->width
;
334 pts
[2].y
+= elem
->height
;
335 pts
[3].y
+= elem
->height
;
337 offs
= elem
->height
* pgram
->shear_grad
;
346 if (pgram
->show_background
) {
347 renderer_ops
->set_fillstyle(renderer
, FILLSTYLE_SOLID
);
349 renderer_ops
->fill_polygon(renderer
,
351 &pgram
->inner_color
);
354 renderer_ops
->set_linewidth(renderer
, pgram
->border_width
);
355 renderer_ops
->set_linestyle(renderer
, pgram
->line_style
);
356 renderer_ops
->set_dashlength(renderer
, pgram
->dashlength
);
357 renderer_ops
->set_linejoin(renderer
, LINEJOIN_MITER
);
359 renderer_ops
->draw_polygon(renderer
,
361 &pgram
->border_color
);
363 text_draw(pgram
->text
, renderer
);
368 pgram_update_data(Pgram
*pgram
, AnchorShape horiz
, AnchorShape vert
)
370 Element
*elem
= &pgram
->element
;
371 ElementBBExtras
*extra
= &elem
->extra_spacing
;
372 DiaObject
*obj
= &elem
->object
;
373 Point center
, bottom_right
;
380 /* save starting points */
381 center
= bottom_right
= elem
->corner
;
382 center
.x
+= elem
->width
/2;
383 bottom_right
.x
+= elem
->width
;
384 center
.y
+= elem
->height
/2;
385 bottom_right
.y
+= elem
->height
;
387 /* this takes the shearing of the parallelogram into account, so the
388 * text can be extend to the edges of the parallelogram */
389 text_calc_boundingbox(pgram
->text
, NULL
);
390 height
= pgram
->text
->height
* pgram
->text
->numlines
+ pgram
->padding
*2 +
392 if (height
> elem
->height
) elem
->height
= height
;
394 avail_width
= elem
->width
- (pgram
->padding
*2 + pgram
->border_width
+
395 fabs(pgram
->shear_grad
) * (elem
->height
+ pgram
->text
->height
396 * pgram
->text
->numlines
));
397 if (avail_width
< pgram
->text
->max_width
) {
398 elem
->width
= (elem
->width
-avail_width
) + pgram
->text
->max_width
;
399 avail_width
= pgram
->text
->max_width
;
403 width = pgram->text->max_width + pgram->padding*2 + pgram->border_width +
404 fabs(pgram->shear_grad) * (elem->height + pgram->text->height
405 * pgram->text->numlines);
406 if (width > elem->width) elem->width = width;
409 /* move shape if necessary ... */
412 elem
->corner
.x
= center
.x
- elem
->width
/2; break;
414 elem
->corner
.x
= bottom_right
.x
- elem
->width
; break;
420 elem
->corner
.y
= center
.y
- elem
->height
/2; break;
422 elem
->corner
.y
= bottom_right
.y
- elem
->height
; break;
428 p
.x
+= elem
->width
/ 2.0;
429 p
.y
+= elem
->height
/ 2.0 - pgram
->text
->height
* pgram
->text
->numlines
/ 2 +
431 switch (pgram
->text
->alignment
) {
433 p
.x
-= avail_width
/2;
436 p
.x
+= avail_width
/2;
441 text_set_position(pgram
->text
, &p
);
443 /* 1/4 of how much more to the left the bottom line is */
444 offs
= -(elem
->height
/ 4.0 * pgram
->shear_grad
);
445 width
= elem
->width
- 4.0*fabs(offs
);
446 top_left
= elem
->corner
.x
;
451 /* Update connections: */
452 connpoint_update(&pgram
->connections
[0],
456 connpoint_update(&pgram
->connections
[1],
457 top_left
+ width
/ 4.0,
460 connpoint_update(&pgram
->connections
[2],
461 top_left
+ width
/ 2.0,
464 connpoint_update(&pgram
->connections
[3],
465 top_left
+ width
* 3.0 / 4.0,
468 connpoint_update(&pgram
->connections
[4],
472 connpoint_update(&pgram
->connections
[5],
474 elem
->corner
.y
+ elem
->height
/ 4.0,
476 connpoint_update(&pgram
->connections
[6],
477 top_left
+ width
+ offs
,
478 elem
->corner
.y
+ elem
->height
/ 4.0,
480 connpoint_update(&pgram
->connections
[7],
481 top_left
+ 2.0 * offs
,
482 elem
->corner
.y
+ elem
->height
/ 2.0,
484 connpoint_update(&pgram
->connections
[8],
485 top_left
+ width
+ 2.0 * offs
,
486 elem
->corner
.y
+ elem
->height
/ 2.0,
488 connpoint_update(&pgram
->connections
[9],
489 top_left
+ 3.0 * offs
,
490 elem
->corner
.y
+ elem
->height
* 3.0 / 4.0,
492 connpoint_update(&pgram
->connections
[10],
493 top_left
+ width
+ 3.0 * offs
,
494 elem
->corner
.y
+ elem
->height
* 3.0 / 4.0,
496 connpoint_update(&pgram
->connections
[11],
497 top_left
+ 4.0 * offs
,
498 elem
->corner
.y
+ elem
->height
,
500 connpoint_update(&pgram
->connections
[12],
501 top_left
+ 4.0 * offs
+ width
/ 4.0,
502 elem
->corner
.y
+ elem
->height
,
504 connpoint_update(&pgram
->connections
[13],
505 top_left
+ 4.0 * offs
+ width
/ 2.0,
506 elem
->corner
.y
+ elem
->height
,
508 connpoint_update(&pgram
->connections
[14],
509 top_left
+ 4.0 * offs
+ 3.0 * width
/ 4.0,
510 elem
->corner
.y
+ elem
->height
,
512 connpoint_update(&pgram
->connections
[15],
513 top_left
+ 4.0 * offs
+ width
,
514 elem
->corner
.y
+ elem
->height
,
516 connpoint_update(&pgram
->connections
[16],
517 top_left
+ 2.0 * offs
+ width
/ 2,
518 elem
->corner
.y
+ elem
->height
/ 2,
521 extra
->border_trans
= pgram
->border_width
/2.0;
522 element_update_boundingbox(elem
);
524 obj
->position
= elem
->corner
;
526 element_update_handles(elem
);
530 pgram_create(Point
*startpoint
,
540 DiaFont
*font
= NULL
;
543 init_default_values();
545 pgram
= g_malloc0(sizeof(Pgram
));
546 elem
= &pgram
->element
;
549 obj
->type
= &pgram_type
;
551 obj
->ops
= &pgram_ops
;
553 elem
->corner
= *startpoint
;
554 elem
->width
= DEFAULT_WIDTH
;
555 elem
->height
= DEFAULT_WIDTH
;
557 pgram
->border_width
= attributes_get_default_linewidth();
558 pgram
->border_color
= attributes_get_foreground();
559 pgram
->inner_color
= attributes_get_background();
560 pgram
->show_background
= default_properties
.show_background
;
561 attributes_get_default_line_style(&pgram
->line_style
, &pgram
->dashlength
);
562 pgram
->shear_angle
= default_properties
.shear_angle
;
563 pgram
->shear_grad
= tan(M_PI
/2.0 - M_PI
/180.0 * pgram
->shear_angle
);
565 pgram
->padding
= default_properties
.padding
;
567 attributes_get_default_font(&font
, &font_height
);
569 p
.x
+= elem
->width
/ 2.0;
570 p
.y
+= elem
->height
/ 2.0 + font_height
/ 2;
571 pgram
->text
= new_text("", font
, font_height
, &p
, &pgram
->border_color
,
573 text_get_attributes(pgram
->text
,&pgram
->attrs
);
574 dia_font_unref(font
);
576 element_init(elem
, 8, NUM_CONNECTIONS
);
578 for (i
=0;i
<NUM_CONNECTIONS
;i
++) {
579 obj
->connections
[i
] = &pgram
->connections
[i
];
580 pgram
->connections
[i
].object
= obj
;
581 pgram
->connections
[i
].connected
= NULL
;
582 pgram
->connections
[i
].flags
= 0;
584 pgram
->connections
[16].flags
= CP_FLAGS_MAIN
;
586 pgram_update_data(pgram
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
589 *handle2
= obj
->handles
[7];
590 return &pgram
->element
.object
;
594 pgram_destroy(Pgram
*pgram
)
596 text_destroy(pgram
->text
);
598 element_destroy(&pgram
->element
);
602 pgram_save(Pgram
*pgram
, ObjectNode obj_node
, const char *filename
)
604 element_save(&pgram
->element
, obj_node
);
606 if (pgram
->border_width
!= 0.1)
607 data_add_real(new_attribute(obj_node
, "border_width"),
608 pgram
->border_width
);
610 if (!color_equals(&pgram
->border_color
, &color_black
))
611 data_add_color(new_attribute(obj_node
, "border_color"),
612 &pgram
->border_color
);
614 if (!color_equals(&pgram
->inner_color
, &color_white
))
615 data_add_color(new_attribute(obj_node
, "inner_color"),
616 &pgram
->inner_color
);
618 data_add_boolean(new_attribute(obj_node
, "show_background"), pgram
->show_background
);
620 if (pgram
->line_style
!= LINESTYLE_SOLID
)
621 data_add_enum(new_attribute(obj_node
, "line_style"),
624 if (pgram
->line_style
!= LINESTYLE_SOLID
&&
625 pgram
->dashlength
!= DEFAULT_LINESTYLE_DASHLEN
)
626 data_add_real(new_attribute(obj_node
, "dashlength"),
629 data_add_real(new_attribute(obj_node
, "shear_angle"),
632 data_add_real(new_attribute(obj_node
, "padding"), pgram
->padding
);
634 data_add_text(new_attribute(obj_node
, "text"), pgram
->text
);
638 pgram_load(ObjectNode obj_node
, int version
, const char *filename
)
646 pgram
= g_malloc0(sizeof(Pgram
));
647 elem
= &pgram
->element
;
650 obj
->type
= &pgram_type
;
651 obj
->ops
= &pgram_ops
;
653 element_load(elem
, obj_node
);
655 pgram
->border_width
= 0.1;
656 attr
= object_find_attribute(obj_node
, "border_width");
658 pgram
->border_width
= data_real( attribute_first_data(attr
) );
660 pgram
->border_color
= color_black
;
661 attr
= object_find_attribute(obj_node
, "border_color");
663 data_color(attribute_first_data(attr
), &pgram
->border_color
);
665 pgram
->inner_color
= color_white
;
666 attr
= object_find_attribute(obj_node
, "inner_color");
668 data_color(attribute_first_data(attr
), &pgram
->inner_color
);
670 pgram
->show_background
= TRUE
;
671 attr
= object_find_attribute(obj_node
, "show_background");
673 pgram
->show_background
= data_boolean( attribute_first_data(attr
) );
675 pgram
->line_style
= LINESTYLE_SOLID
;
676 attr
= object_find_attribute(obj_node
, "line_style");
678 pgram
->line_style
= data_enum( attribute_first_data(attr
) );
680 pgram
->dashlength
= DEFAULT_LINESTYLE_DASHLEN
;
681 attr
= object_find_attribute(obj_node
, "dashlength");
683 pgram
->dashlength
= data_real(attribute_first_data(attr
));
685 pgram
->shear_angle
= 0.0;
686 attr
= object_find_attribute(obj_node
, "shear_angle");
688 pgram
->shear_angle
= data_real( attribute_first_data(attr
) );
689 pgram
->shear_grad
= tan(M_PI
/2.0 - M_PI
/180.0 * pgram
->shear_angle
);
691 pgram
->padding
= default_properties
.padding
;
692 attr
= object_find_attribute(obj_node
, "padding");
694 pgram
->padding
= data_real( attribute_first_data(attr
) );
697 attr
= object_find_attribute(obj_node
, "text");
699 pgram
->text
= data_text(attribute_first_data(attr
));
701 element_init(elem
, 8, NUM_CONNECTIONS
);
703 for (i
=0;i
<NUM_CONNECTIONS
;i
++) {
704 obj
->connections
[i
] = &pgram
->connections
[i
];
705 pgram
->connections
[i
].object
= obj
;
706 pgram
->connections
[i
].connected
= NULL
;
707 pgram
->connections
[i
].flags
= 0;
709 pgram
->connections
[16].flags
= CP_FLAGS_MAIN
;
711 pgram_update_data(pgram
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
713 return &pgram
->element
.object
;