2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / Jackson / domain.c
bloba79ea6094ca939cb2126a5b642b57ea535a294fe
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.
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
32 #include <stdio.h>
33 #include <assert.h>
34 #include <math.h>
35 #include <string.h>
36 #include <glib.h>
38 #include "intl.h"
39 #include "object.h"
40 #include "element.h"
41 #include "connectionpoint.h"
42 #include "diarenderer.h"
43 #include "attributes.h"
44 #include "text.h"
45 #include "widgets.h"
46 #include "message.h"
47 #include "connpoint_line.h"
48 #include "color.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
64 typedef enum {
65 ANCHOR_MIDDLE,
66 ANCHOR_START,
67 ANCHOR_END
68 } AnchorShape;
70 /* Domain Type */
72 typedef enum {
73 DOMAIN_GIVEN,
74 DOMAIN_DESIGNED,
75 DOMAIN_MACHINE
76 } DomainType;
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 },
82 { NULL, 0}
85 /* Domain qualifier */
87 typedef enum {
88 DOMAIN_NONE,
89 DOMAIN_CAUSAL,
90 DOMAIN_BIDDABLE,
91 DOMAIN_LEXICAL
92 } DomainKind;
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 },
100 { NULL, 0}
103 typedef struct _Box {
104 Element element;
106 ConnPointLine *north,*south,*east,*west;
108 Text *text;
109 real padding;
110 DomainType domtype;
111 DomainKind domkind;
113 TextAttributes attrs;
114 int init; /* workaround for property bug */
115 } Box;
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,
127 void *user_data,
128 Handle **handle1,
129 Handle **handle2);
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 */
151 0, /* version */
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,
176 N_("Domain Type"),
177 N_("Domain Type"),
178 prop_domain_type_data},
180 { "domkind", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
181 N_("Domain Kind"),
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,
192 PROP_STD_TEXT_FONT,
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},
202 PROP_DESC_END
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);
211 return 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)},
227 {NULL}
230 static void
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,
235 box_offsets,props);
238 static void
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,
244 box_offsets,props);
246 apply_textattr_properties(props,box->text,"text",&box->attrs);
247 jackson_box_update_data(box, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
251 static real
252 jackson_box_distance_from(Box *box, Point *point)
254 Element *elem = &box->element;
255 Rectangle rect;
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);
265 static void
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);
274 static ObjectChange*
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;
281 assert(box!=NULL);
282 assert(handle!=NULL);
283 assert(to!=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;
304 default:
305 break;
307 jackson_box_update_data(box, horiz, vert);
308 return NULL;
311 static ObjectChange*
312 jackson_box_move(Box *box, Point *to)
314 box->element.corner = *to;
316 jackson_box_update_data(box, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
317 return NULL;
320 /* draw method */
321 static void
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;
326 Element *elem;
327 real idfontheight;
328 char* s;
330 /* some asserts */
331 assert(box != NULL);
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;
344 p1b.x= p1t.x;
345 p1b.y= p1t.y + elem->height;
347 p2t.x= elem->corner.x + LEFT_SPACE;
348 p2t.y= p1t.y;
349 p2b.x= p2t.x;
350 p2b.y= p1b.y;
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);
374 b2 = b3 = b1;
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;
384 default: s=NULL;
387 if (s!=NULL) {
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);
395 /* resize stuff */
396 static void
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;
403 Point p;
404 real width, height;
405 Point nw,ne,se,sw;
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 ... */
422 switch (horiz) {
423 case ANCHOR_MIDDLE:
424 elem->corner.x = center.x - elem->width/2; break;
425 case ANCHOR_END:
426 elem->corner.x = bottom_right.x - elem->width; break;
427 default:
428 break;
431 switch (vert) {
432 case ANCHOR_MIDDLE:
433 elem->corner.y = center.y - elem->height/2; break;
434 case ANCHOR_END:
435 elem->corner.y = bottom_right.y - elem->height; break;
436 default:
437 break;
440 p = elem->corner;
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: */
453 nw = elem->corner;
454 se.x = nw.x + elem->width;
455 se.y = nw.y + elem->height;
456 ne.x = se.x;
457 ne.y = nw.y;
458 sw.y = se.y;
459 sw.x = nw.x;
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)
475 ConnPointLine *cpl;
476 real dist,dist2;
478 cpl = box->north;
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);
482 if (dist2 < dist) {
483 cpl = box->west;
484 dist = dist2;
486 dist2 = distance_line_point(&box->south->start,&box->south->end,0,clicked);
487 if (dist2 < dist) {
488 cpl = box->south;
489 dist = dist2;
491 dist2 = distance_line_point(&box->east->start,&box->east->end,0,clicked);
492 if (dist2 < dist) {
493 cpl = box->east;
494 /*dist = dist2;*/
496 return cpl;
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;
508 ConnPointLine *cpl;
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;
521 ConnPointLine *cpl;
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,
533 NULL, 1 },
536 static DiaMenu object_menu = {
537 N_("Jackson domain"),
538 sizeof(object_menu_items)/sizeof(DiaMenuItem),
539 object_menu_items,
540 NULL
543 static DiaMenu *
544 jackson_box_get_object_menu(Box *box, Point *clickedpoint)
546 ConnPointLine *cpl;
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);
552 return &object_menu;
555 /* create */
556 static DiaObject *
557 jackson_box_create(Point *startpoint,
558 void *user_data,
559 Handle **handle1,
560 Handle **handle2)
562 Box *box;
563 Element *elem;
564 DiaObject *obj;
565 Point p;
566 DiaFont* font;
568 box = g_malloc0(sizeof(Box));
569 elem = &box->element;
570 obj = &elem->object;
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;
581 /* text stuff */
582 p = *startpoint;
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,
589 DEFAULT_FONT, &p,
590 &color_black,
591 ALIGN_CENTER);
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);
604 *handle1 = NULL;
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;
623 static void
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);
637 static DiaObject *
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);