1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * Jackson diagram - adapted by Christophe Ponsard
5 * This class captures all kind of domains (given, designed, machine)
6 * both for generic problems and for problem frames (ie. with domain kinds)
8 * based on SADT diagrams copyright (C) 2000, 2001 Cyrille Chepelov
10 * Forked from Flowchart toolbox -- objects for drawing flowcharts.
11 * Copyright (C) 1999 James Henstridge.
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
41 #include "connectionpoint.h"
42 #include "diarenderer.h"
43 #include "attributes.h"
47 #include "connpoint_line.h"
49 #include "properties.h"
51 #include "pixmaps/given_domain.xpm"
53 #define DEFAULT_WIDTH 3.0
54 #define DEFAULT_HEIGHT 1.0
55 #define DEFAULT_BORDER 0.25
56 #define DEFAULT_PADDING 0.4
57 #define DEFAULT_FONT 0.7
58 #define JACKSON_BOX_LINE_WIDTH 0.09
59 #define JACKSON_BOX_FG_COLOR color_black
60 #define JACKSON_BOX_BG_COLOR color_white
61 #define LEFT_SPACE 0.7
62 #define RIGHT_SPACE 0.3
78 static PropEnumData prop_domain_type_data
[] = {
79 { N_("Given Domain"), DOMAIN_GIVEN
},
80 { N_("Designed Domain"), DOMAIN_DESIGNED
},
81 { N_("Machine Domain"), DOMAIN_MACHINE
},
85 /* Domain qualifier */
95 static PropEnumData prop_domain_kind_data
[] = {
96 { N_("None"), DOMAIN_NONE
},
97 { N_("Causal"), DOMAIN_CAUSAL
},
98 { N_("Biddable"), DOMAIN_BIDDABLE
},
99 { N_("Lexical"), DOMAIN_LEXICAL
},
103 typedef struct _Box
{
106 ConnPointLine
*north
,*south
,*east
,*west
;
113 TextAttributes attrs
;
114 int init
; /* workaround for property bug */
117 static real
jackson_box_distance_from(Box
*box
, Point
*point
);
118 static void jackson_box_select(Box
*box
, Point
*clicked_point
,
119 DiaRenderer
*interactive_renderer
);
120 static ObjectChange
* jackson_box_move_handle(Box
*box
, Handle
*handle
,
121 Point
*to
, ConnectionPoint
*cp
,
122 HandleMoveReason reason
, ModifierKeys modifiers
);
123 static ObjectChange
* jackson_box_move(Box
*box
, Point
*to
);
124 static void jackson_box_draw(Box
*box
, DiaRenderer
*renderer
);
125 static void jackson_box_update_data(Box
*box
, AnchorShape horix
, AnchorShape vert
);
126 static DiaObject
*jackson_box_create(Point
*startpoint
,
130 static void jackson_box_destroy(Box
*box
);
131 static DiaObject
*jackson_box_load(ObjectNode obj_node
, int version
,
132 const char *filename
);
133 static DiaMenu
*jackson_box_get_object_menu(Box
*box
, Point
*clickedpoint
);
135 static PropDescription
*jackson_box_describe_props(Box
*box
);
136 static void jackson_box_get_props(Box
*box
, GPtrArray
*props
);
137 static void jackson_box_set_props(Box
*box
, GPtrArray
*props
);
139 static ObjectTypeOps jackson_domain_type_ops
=
141 (CreateFunc
) jackson_box_create
,
142 (LoadFunc
) jackson_box_load
/*using_properties*/,
143 (SaveFunc
) object_save_using_properties
,
144 (GetDefaultsFunc
) NULL
,
145 (ApplyDefaultsFunc
) NULL
,
148 DiaObjectType jackson_domain_type
=
150 "Jackson - domain", /* name */
152 (char **) jackson_given_domain_xpm
, /* this is the default pixmap */
153 &jackson_domain_type_ops
/* ops */
156 static ObjectOps jackson_box_ops
= {
157 (DestroyFunc
) jackson_box_destroy
,
158 (DrawFunc
) jackson_box_draw
,
159 (DistanceFunc
) jackson_box_distance_from
,
160 (SelectFunc
) jackson_box_select
,
161 (CopyFunc
) object_copy_using_properties
,
162 (MoveFunc
) jackson_box_move
,
163 (MoveHandleFunc
) jackson_box_move_handle
,
164 (GetPropertiesFunc
) object_create_props_dialog
,
165 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
166 (ObjectMenuFunc
) jackson_box_get_object_menu
,
167 (DescribePropsFunc
) jackson_box_describe_props
,
168 (GetPropsFunc
) jackson_box_get_props
,
169 (SetPropsFunc
) jackson_box_set_props
172 static PropDescription box_props
[] = {
173 ELEMENT_COMMON_PROPERTIES
,
175 { "domtype", PROP_TYPE_ENUM
, PROP_FLAG_VISIBLE
,
178 prop_domain_type_data
},
180 { "domkind", PROP_TYPE_ENUM
, PROP_FLAG_VISIBLE
,
182 N_("Optional kind which appears in the lower right corner of the Domain"),
183 prop_domain_kind_data
},
185 /* hidding style stuff
186 { "padding",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
187 N_("Text padding"), NULL, &text_padding_data},
189 { "text", PROP_TYPE_TEXT
, 0,NULL
,NULL
},
191 PROP_STD_TEXT_ALIGNMENT,
193 PROP_STD_TEXT_HEIGHT,
194 PROP_STD_TEXT_COLOUR,
197 { "cpl_north",PROP_TYPE_CONNPOINT_LINE
, 0, NULL
, NULL
},
198 { "cpl_west",PROP_TYPE_CONNPOINT_LINE
, 0, NULL
, NULL
},
199 { "cpl_south",PROP_TYPE_CONNPOINT_LINE
, 0, NULL
, NULL
},
200 { "cpl_east",PROP_TYPE_CONNPOINT_LINE
, 0, NULL
, NULL
},
205 static PropDescription
*
206 jackson_box_describe_props(Box
*box
)
208 if (box_props
[0].quark
== 0) {
209 prop_desc_list_calculate_quarks(box_props
);
214 static PropOffset box_offsets
[] = {
215 ELEMENT_COMMON_PROPERTIES_OFFSETS
,
216 { "domtype", PROP_TYPE_ENUM
, offsetof(Box
,domtype
)},
217 { "domkind", PROP_TYPE_ENUM
, offsetof(Box
,domkind
)},
218 { "text", PROP_TYPE_TEXT
, offsetof(Box
,text
)},
219 { "text_alignment",PROP_TYPE_ENUM
,offsetof(Box
,attrs
.alignment
)},
220 { "text_font",PROP_TYPE_FONT
,offsetof(Box
,attrs
.font
)},
221 { "text_height",PROP_TYPE_REAL
,offsetof(Box
,attrs
.height
)},
222 { "text_colour",PROP_TYPE_COLOUR
,offsetof(Box
,attrs
.color
)},
223 { "cpl_north",PROP_TYPE_CONNPOINT_LINE
, offsetof(Box
,north
)},
224 { "cpl_west",PROP_TYPE_CONNPOINT_LINE
, offsetof(Box
,west
)},
225 { "cpl_south",PROP_TYPE_CONNPOINT_LINE
, offsetof(Box
,south
)},
226 { "cpl_east",PROP_TYPE_CONNPOINT_LINE
, offsetof(Box
,east
)},
231 jackson_box_get_props(Box
*box
, GPtrArray
*props
)
233 text_get_attributes(box
->text
,&box
->attrs
);
234 object_get_props_from_offsets(&box
->element
.object
,
239 jackson_box_set_props(Box
*box
, GPtrArray
*props
)
241 if (box
->init
==-1) { box
->init
++; return; } /* workaround init bug */
243 object_set_props_from_offsets(&box
->element
.object
,
246 apply_textattr_properties(props
,box
->text
,"text",&box
->attrs
);
247 jackson_box_update_data(box
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
252 jackson_box_distance_from(Box
*box
, Point
*point
)
254 Element
*elem
= &box
->element
;
257 rect
.left
= elem
->corner
.x
- JACKSON_BOX_LINE_WIDTH
/2;
258 rect
.right
= elem
->corner
.x
+ elem
->width
+ JACKSON_BOX_LINE_WIDTH
/2;
259 rect
.top
= elem
->corner
.y
- JACKSON_BOX_LINE_WIDTH
/2;
260 rect
.bottom
= elem
->corner
.y
+ elem
->height
+ JACKSON_BOX_LINE_WIDTH
/2;
262 return distance_rectangle_point(&rect
, point
);
266 jackson_box_select(Box
*box
, Point
*clicked_point
,
267 DiaRenderer
*interactive_renderer
)
269 text_set_cursor(box
->text
, clicked_point
, interactive_renderer
);
270 text_grab_focus(box
->text
, &box
->element
.object
);
271 element_update_handles(&box
->element
);
275 jackson_box_move_handle(Box
*box
, Handle
*handle
,
276 Point
*to
, ConnectionPoint
*cp
,
277 HandleMoveReason reason
, ModifierKeys modifiers
)
279 AnchorShape horiz
= ANCHOR_MIDDLE
, vert
= ANCHOR_MIDDLE
;
282 assert(handle
!=NULL
);
285 element_move_handle(&box
->element
, handle
->id
, to
, cp
, reason
, modifiers
);
287 switch (handle
->id
) {
288 case HANDLE_RESIZE_NW
:
289 horiz
= ANCHOR_END
; vert
= ANCHOR_END
; break;
290 case HANDLE_RESIZE_N
:
291 vert
= ANCHOR_END
; break;
292 case HANDLE_RESIZE_NE
:
293 horiz
= ANCHOR_START
; vert
= ANCHOR_END
; break;
294 case HANDLE_RESIZE_E
:
295 horiz
= ANCHOR_START
; break;
296 case HANDLE_RESIZE_SE
:
297 horiz
= ANCHOR_START
; vert
= ANCHOR_START
; break;
298 case HANDLE_RESIZE_S
:
299 vert
= ANCHOR_START
; break;
300 case HANDLE_RESIZE_SW
:
301 horiz
= ANCHOR_END
; vert
= ANCHOR_START
; break;
302 case HANDLE_RESIZE_W
:
303 horiz
= ANCHOR_END
; break;
307 jackson_box_update_data(box
, horiz
, vert
);
312 jackson_box_move(Box
*box
, Point
*to
)
314 box
->element
.corner
= *to
;
316 jackson_box_update_data(box
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
322 jackson_box_draw(Box
*box
, DiaRenderer
*renderer
)
324 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
325 Point b0
,b1
,b2
,b3
,p1t
,p1b
,p2t
,p2b
;
332 assert(renderer
!= NULL
);
334 /* computing positions */
335 elem
= &box
->element
;
337 b0
.x
= elem
->corner
.x
;
338 b0
.y
= elem
->corner
.y
;
339 b1
.x
= b0
.x
+ elem
->width
;
340 b1
.y
= b0
.y
+ elem
->height
;
342 p1t
.x
= elem
->corner
.x
+ LEFT_SPACE
/2;
343 p1t
.y
= elem
->corner
.y
;
345 p1b
.y
= p1t
.y
+ elem
->height
;
347 p2t
.x
= elem
->corner
.x
+ LEFT_SPACE
;
352 /* drawing main box */
353 renderer_ops
->set_fillstyle(renderer
, FILLSTYLE_SOLID
);
354 renderer_ops
->fill_rect(renderer
, &b0
, &b1
, &JACKSON_BOX_BG_COLOR
);
356 renderer_ops
->set_linewidth(renderer
, JACKSON_BOX_LINE_WIDTH
);
357 renderer_ops
->set_linestyle(renderer
, LINESTYLE_SOLID
);
358 renderer_ops
->set_linejoin(renderer
, LINEJOIN_MITER
);
360 renderer_ops
->draw_rect(renderer
, &b0
, &b1
, &JACKSON_BOX_FG_COLOR
);
362 /* adding lines for designed/machine domains */
363 if (box
->domtype
!=DOMAIN_GIVEN
) {
364 renderer_ops
->draw_line(renderer
, &p1t
, &p1b
, &JACKSON_BOX_FG_COLOR
);
367 if (box
->domtype
==DOMAIN_MACHINE
) {
368 renderer_ops
->draw_line(renderer
, &p2t
, &p2b
, &JACKSON_BOX_FG_COLOR
);
371 /* adding corner for optional qualifier */
372 idfontheight
= box
->text
->height
;
373 renderer_ops
->set_font(renderer
, box
->text
->font
, idfontheight
);
375 b2
.x
-= .2 * idfontheight
;
376 b2
.y
-= .2 * idfontheight
;
377 b3
.x
-= idfontheight
;
378 b3
.y
-= idfontheight
;
380 switch (box
->domkind
) {
381 case DOMAIN_CAUSAL
: s
=g_strdup("C"); break;
382 case DOMAIN_BIDDABLE
: s
=g_strdup("B"); break;
383 case DOMAIN_LEXICAL
: s
=g_strdup("L"); break;
388 renderer_ops
->draw_rect(renderer
, &b3
, &b1
, &JACKSON_BOX_FG_COLOR
);
389 renderer_ops
->draw_string(renderer
, &(*s
), &b2
, ALIGN_RIGHT
, &box
->text
->color
);
392 text_draw(box
->text
, renderer
);
397 jackson_box_update_data(Box
*box
, AnchorShape horiz
, AnchorShape vert
)
399 Element
*elem
= &box
->element
;
400 ElementBBExtras
*extra
= &elem
->extra_spacing
;
401 DiaObject
*obj
= &elem
->object
;
402 Point center
, bottom_right
;
407 /* save starting points */
408 center
= bottom_right
= elem
->corner
;
409 center
.x
+= elem
->width
/2;
410 bottom_right
.x
+= elem
->width
;
411 center
.y
+= elem
->height
/2;
412 bottom_right
.y
+= elem
->height
;
414 text_calc_boundingbox(box
->text
, NULL
);
415 width
= LEFT_SPACE
+ box
->text
->max_width
+ box
->padding
*2 + RIGHT_SPACE
;
416 height
= box
->text
->height
* box
->text
->numlines
+ box
->padding
*2;
418 if (width
> elem
->width
) elem
->width
= width
;
419 if (height
> elem
->height
) elem
->height
= height
;
421 /* move shape if necessary ... */
424 elem
->corner
.x
= center
.x
- elem
->width
/2; break;
426 elem
->corner
.x
= bottom_right
.x
- elem
->width
; break;
433 elem
->corner
.y
= center
.y
- elem
->height
/2; break;
435 elem
->corner
.y
= bottom_right
.y
- elem
->height
; break;
441 p
.x
+= (LEFT_SPACE
+elem
->width
-RIGHT_SPACE
) / 2.0;
442 p
.y
+= elem
->height
/ 2.0 - box
->text
->height
* box
->text
->numlines
/ 2 + box
->text
->ascent
;
443 text_set_position(box
->text
, &p
);
445 extra
->border_trans
= JACKSON_BOX_LINE_WIDTH
/ 2.0;
446 element_update_boundingbox(elem
);
448 obj
->position
= elem
->corner
;
450 element_update_handles(elem
);
452 /* Update connections: */
454 se
.x
= nw
.x
+ elem
->width
;
455 se
.y
= nw
.y
+ elem
->height
;
461 connpointline_update(box
->north
);
462 connpointline_putonaline(box
->north
,&ne
,&nw
);
463 connpointline_update(box
->west
);
464 connpointline_putonaline(box
->west
,&nw
,&sw
);
465 connpointline_update(box
->south
);
466 connpointline_putonaline(box
->south
,&sw
,&se
);
467 connpointline_update(box
->east
);
468 connpointline_putonaline(box
->east
,&se
,&ne
);
472 static ConnPointLine
*
473 jackson_box_get_clicked_border(Box
*box
, Point
*clicked
)
479 dist
= distance_line_point(&box
->north
->start
,&box
->north
->end
,0,clicked
);
481 dist2
= distance_line_point(&box
->west
->start
,&box
->west
->end
,0,clicked
);
486 dist2
= distance_line_point(&box
->south
->start
,&box
->south
->end
,0,clicked
);
491 dist2
= distance_line_point(&box
->east
->start
,&box
->east
->end
,0,clicked
);
499 inline static ObjectChange
*
500 jackson_box_create_change(Box
*box
, ObjectChange
*inner
, ConnPointLine
*cpl
) {
501 return (ObjectChange
*)inner
;
504 static ObjectChange
*
505 jackson_box_add_connpoint_callback(DiaObject
*obj
, Point
*clicked
, gpointer data
)
507 ObjectChange
*change
;
509 Box
*box
= (Box
*)obj
;
511 cpl
= jackson_box_get_clicked_border(box
,clicked
);
512 change
= connpointline_add_point(cpl
, clicked
);
513 jackson_box_update_data((Box
*)obj
,ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
514 return jackson_box_create_change(box
,change
,cpl
);
517 static ObjectChange
*
518 jackson_box_remove_connpoint_callback(DiaObject
*obj
, Point
*clicked
, gpointer data
)
520 ObjectChange
*change
;
522 Box
*box
= (Box
*)obj
;
524 cpl
= jackson_box_get_clicked_border(box
,clicked
);
525 change
= connpointline_remove_point(cpl
, clicked
);
526 jackson_box_update_data((Box
*)obj
,ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
527 return jackson_box_create_change(box
,change
,cpl
);
530 static DiaMenuItem object_menu_items
[] = {
531 { N_("Add connection point"), jackson_box_add_connpoint_callback
, NULL
, 1 },
532 { N_("Delete connection point"), jackson_box_remove_connpoint_callback
,
536 static DiaMenu object_menu
= {
537 N_("Jackson domain"),
538 sizeof(object_menu_items
)/sizeof(DiaMenuItem
),
544 jackson_box_get_object_menu(Box
*box
, Point
*clickedpoint
)
548 cpl
= jackson_box_get_clicked_border(box
,clickedpoint
);
549 /* Set entries sensitive/selected etc here */
550 object_menu_items
[0].active
= connpointline_can_add_point(cpl
, clickedpoint
);
551 object_menu_items
[1].active
= connpointline_can_remove_point(cpl
, clickedpoint
);
557 jackson_box_create(Point
*startpoint
,
568 box
= g_malloc0(sizeof(Box
));
569 elem
= &box
->element
;
572 obj
->type
= &jackson_domain_type
;
573 obj
->ops
= &jackson_box_ops
;
575 elem
->corner
= *startpoint
;
576 elem
->width
= DEFAULT_WIDTH
;
577 elem
->height
= DEFAULT_HEIGHT
;
579 box
->padding
= DEFAULT_PADDING
;
583 p
.x
+= (LEFT_SPACE
+elem
->width
) / 2.0;
584 p
.y
+= elem
->height
/ 2.0 + DEFAULT_FONT
/ 2;
586 font
= dia_font_new_from_style( DIA_FONT_SANS
, DEFAULT_FONT
);
588 box
->text
= new_text("", font
,
592 dia_font_unref(font
);
594 element_init(elem
, 8, 0);
596 box
->north
= connpointline_create(obj
,3);
597 box
->west
= connpointline_create(obj
,1);
598 box
->south
= connpointline_create(obj
,3);
599 box
->east
= connpointline_create(obj
,1);
601 box
->element
.extra_spacing
.border_trans
= JACKSON_BOX_LINE_WIDTH
/2.0;
602 jackson_box_update_data(box
, ANCHOR_MIDDLE
, ANCHOR_MIDDLE
);
605 *handle2
= obj
->handles
[7];
607 /* template information here */
609 switch (GPOINTER_TO_INT(user_data
)) {
610 case 1: box
->domtype
=DOMAIN_GIVEN
; break;
611 case 2: box
->domtype
=DOMAIN_DESIGNED
; break;
612 case 3: box
->domtype
=DOMAIN_MACHINE
; break;
613 default: box
->domtype
=DOMAIN_GIVEN
; break;
616 box
->domkind
=DOMAIN_NONE
;
618 if (GPOINTER_TO_INT(user_data
)!=0) box
->init
=-1; else box
->init
=0;
620 return &box
->element
.object
;
624 jackson_box_destroy(Box
*box
)
626 text_destroy(box
->text
);
628 connpointline_destroy(box
->east
);
629 connpointline_destroy(box
->south
);
630 connpointline_destroy(box
->west
);
631 connpointline_destroy(box
->north
);
633 element_destroy(&box
->element
);
638 jackson_box_load(ObjectNode obj_node
, int version
, const char *filename
)
640 return object_load_using_properties(&jackson_domain_type
,
641 obj_node
,version
,filename
);