2002-06-23 Hans Breuer <hans@breuer.org>
[dia.git] / objects / SADT / box.c
bloba886b205a95263036ddf0e13615bb6b0a7324491
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * SADT Activity/Data box -- objects for drawing SADT diagrams.
5 * Copyright (C) 2000, 2001 Cyrille Chepelov
6 *
7 * Forked from Flowchart toolbox -- objects for drawing flowcharts.
8 * Copyright (C) 1999 James Henstridge.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include <assert.h>
30 #include <gtk/gtk.h>
31 #include <math.h>
32 #include <string.h>
33 #include <glib.h>
35 #include "intl.h"
36 #include "object.h"
37 #include "element.h"
38 #include "connectionpoint.h"
39 #include "render.h"
40 #include "attributes.h"
41 #include "text.h"
42 #include "widgets.h"
43 #include "message.h"
44 #include "connpoint_line.h"
45 #include "color.h"
46 #include "properties.h"
48 #include "pixmaps/sadtbox.xpm"
50 #define DEFAULT_WIDTH 7.0
51 #define DEFAULT_HEIGHT 5.0
52 #define DEFAULT_BORDER 0.25
53 #define SADTBOX_LINE_WIDTH 0.10
54 #define SADTBOX_FG_COLOR color_black
55 #define SADTBOX_BG_COLOR color_white
57 typedef enum {
58 ANCHOR_MIDDLE,
59 ANCHOR_START,
60 ANCHOR_END
61 } AnchorShape;
64 typedef struct _Box {
65 Element element;
67 ConnPointLine *north,*south,*east,*west;
69 Text *text;
70 gchar *id;
71 real padding;
73 TextAttributes attrs;
74 } Box;
76 static real sadtbox_distance_from(Box *box, Point *point);
77 static void sadtbox_select(Box *box, Point *clicked_point,
78 Renderer *interactive_renderer);
79 static void sadtbox_move_handle(Box *box, Handle *handle,
80 Point *to, HandleMoveReason reason,
81 ModifierKeys modifiers);
82 static void sadtbox_move(Box *box, Point *to);
83 static void sadtbox_draw(Box *box, Renderer *renderer);
84 static void sadtbox_update_data(Box *box, AnchorShape horix, AnchorShape vert);
85 static Object *sadtbox_create(Point *startpoint,
86 void *user_data,
87 Handle **handle1,
88 Handle **handle2);
89 static void sadtbox_destroy(Box *box);
90 static Object *sadtbox_load(ObjectNode obj_node, int version,
91 const char *filename);
92 static DiaMenu *sadtbox_get_object_menu(Box *box, Point *clickedpoint);
94 static PropDescription *sadtbox_describe_props(Box *box);
95 static void sadtbox_get_props(Box *box, GPtrArray *props);
96 static void sadtbox_set_props(Box *box, GPtrArray *props);
98 static ObjectTypeOps sadtbox_type_ops =
100 (CreateFunc) sadtbox_create,
101 (LoadFunc) sadtbox_load/*using_properties*/,
102 (SaveFunc) object_save_using_properties,
103 (GetDefaultsFunc) NULL,
104 (ApplyDefaultsFunc) NULL,
107 ObjectType sadtbox_type =
109 "SADT - box", /* name */
110 0, /* version */
111 (char **) sadtbox_xpm, /* pixmap */
113 &sadtbox_type_ops /* ops */
116 static ObjectOps sadtbox_ops = {
117 (DestroyFunc) sadtbox_destroy,
118 (DrawFunc) sadtbox_draw,
119 (DistanceFunc) sadtbox_distance_from,
120 (SelectFunc) sadtbox_select,
121 (CopyFunc) object_copy_using_properties,
122 (MoveFunc) sadtbox_move,
123 (MoveHandleFunc) sadtbox_move_handle,
124 (GetPropertiesFunc) object_create_props_dialog,
125 (ApplyPropertiesFunc) object_apply_props_from_dialog,
126 (ObjectMenuFunc) sadtbox_get_object_menu,
127 (DescribePropsFunc) sadtbox_describe_props,
128 (GetPropsFunc) sadtbox_get_props,
129 (SetPropsFunc) sadtbox_set_props
132 static PropNumData text_padding_data = { 0.0, 10.0, 0.1 };
134 static PropDescription box_props[] = {
135 ELEMENT_COMMON_PROPERTIES,
136 { "padding",PROP_TYPE_REAL,PROP_FLAG_VISIBLE,
137 N_("Text padding"), NULL, &text_padding_data},
138 { "text", PROP_TYPE_TEXT, 0,NULL,NULL},
139 PROP_STD_TEXT_ALIGNMENT,
140 PROP_STD_TEXT_FONT,
141 PROP_STD_TEXT_HEIGHT,
142 PROP_STD_TEXT_COLOUR,
143 { "id", PROP_TYPE_STRING, PROP_FLAG_VISIBLE|PROP_FLAG_DONT_MERGE,
144 N_("Activity/Data identifier"),
145 N_("The identifier which appears in the lower right corner of the Box")},
146 { "cpl_north",PROP_TYPE_CONNPOINT_LINE, 0, NULL, NULL},
147 { "cpl_west",PROP_TYPE_CONNPOINT_LINE, 0, NULL, NULL},
148 { "cpl_south",PROP_TYPE_CONNPOINT_LINE, 0, NULL, NULL},
149 { "cpl_east",PROP_TYPE_CONNPOINT_LINE, 0, NULL, NULL},
150 PROP_DESC_END
153 static PropDescription *
154 sadtbox_describe_props(Box *box)
156 if (box_props[0].quark == 0) {
157 prop_desc_list_calculate_quarks(box_props);
159 return box_props;
162 static PropOffset box_offsets[] = {
163 ELEMENT_COMMON_PROPERTIES_OFFSETS,
164 { "padding",PROP_TYPE_REAL,offsetof(Box,padding)},
165 { "text", PROP_TYPE_TEXT, offsetof(Box,text)},
166 { "text_alignment",PROP_TYPE_ENUM,offsetof(Box,attrs.alignment)},
167 { "text_font",PROP_TYPE_FONT,offsetof(Box,attrs.font)},
168 { "text_height",PROP_TYPE_REAL,offsetof(Box,attrs.height)},
169 { "text_colour",PROP_TYPE_COLOUR,offsetof(Box,attrs.color)},
170 { "id", PROP_TYPE_STRING, offsetof(Box,id)},
171 { "cpl_north",PROP_TYPE_CONNPOINT_LINE, offsetof(Box,north)},
172 { "cpl_west",PROP_TYPE_CONNPOINT_LINE, offsetof(Box,west)},
173 { "cpl_south",PROP_TYPE_CONNPOINT_LINE, offsetof(Box,south)},
174 { "cpl_east",PROP_TYPE_CONNPOINT_LINE, offsetof(Box,east)},
175 {NULL}
178 static void
179 sadtbox_get_props(Box *box, GPtrArray *props)
181 text_get_attributes(box->text,&box->attrs);
182 object_get_props_from_offsets(&box->element.object,
183 box_offsets,props);
186 static void
187 sadtbox_set_props(Box *box, GPtrArray *props)
189 object_set_props_from_offsets(&box->element.object,
190 box_offsets,props);
191 apply_textattr_properties(props,box->text,"text",&box->attrs);
192 sadtbox_update_data(box, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
195 static real
196 sadtbox_distance_from(Box *box, Point *point)
198 Element *elem = &box->element;
199 Rectangle rect;
201 rect.left = elem->corner.x - SADTBOX_LINE_WIDTH/2;
202 rect.right = elem->corner.x + elem->width + SADTBOX_LINE_WIDTH/2;
203 rect.top = elem->corner.y - SADTBOX_LINE_WIDTH/2;
204 rect.bottom = elem->corner.y + elem->height + SADTBOX_LINE_WIDTH/2;
205 return distance_rectangle_point(&rect, point);
208 static void
209 sadtbox_select(Box *box, Point *clicked_point,
210 Renderer *interactive_renderer)
212 text_set_cursor(box->text, clicked_point, interactive_renderer);
213 text_grab_focus(box->text, &box->element.object);
214 element_update_handles(&box->element);
217 static void
218 sadtbox_move_handle(Box *box, Handle *handle,
219 Point *to, HandleMoveReason reason, ModifierKeys modifiers)
221 AnchorShape horiz = ANCHOR_MIDDLE, vert = ANCHOR_MIDDLE;
223 assert(box!=NULL);
224 assert(handle!=NULL);
225 assert(to!=NULL);
227 element_move_handle(&box->element, handle->id, to, reason);
229 switch (handle->id) {
230 case HANDLE_RESIZE_NW:
231 horiz = ANCHOR_END; vert = ANCHOR_END; break;
232 case HANDLE_RESIZE_N:
233 vert = ANCHOR_END; break;
234 case HANDLE_RESIZE_NE:
235 horiz = ANCHOR_START; vert = ANCHOR_END; break;
236 case HANDLE_RESIZE_E:
237 horiz = ANCHOR_START; break;
238 case HANDLE_RESIZE_SE:
239 horiz = ANCHOR_START; vert = ANCHOR_START; break;
240 case HANDLE_RESIZE_S:
241 vert = ANCHOR_START; break;
242 case HANDLE_RESIZE_SW:
243 horiz = ANCHOR_END; vert = ANCHOR_START; break;
244 case HANDLE_RESIZE_W:
245 horiz = ANCHOR_END; break;
246 default:
247 break;
249 sadtbox_update_data(box, horiz, vert);
252 static void
253 sadtbox_move(Box *box, Point *to)
255 box->element.corner = *to;
257 sadtbox_update_data(box, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
260 static void
261 sadtbox_draw(Box *box, Renderer *renderer)
263 Point lr_corner,pos;
264 Element *elem;
265 real idfontheight;
267 assert(box != NULL);
268 assert(renderer != NULL);
270 elem = &box->element;
272 lr_corner.x = elem->corner.x + elem->width;
273 lr_corner.y = elem->corner.y + elem->height;
275 renderer->ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
276 renderer->ops->fill_rect(renderer,
277 &elem->corner,
278 &lr_corner,
279 &SADTBOX_BG_COLOR);
282 renderer->ops->set_linewidth(renderer, SADTBOX_LINE_WIDTH);
283 renderer->ops->set_linestyle(renderer, LINESTYLE_SOLID);
284 renderer->ops->set_linejoin(renderer, LINEJOIN_MITER);
286 renderer->ops->draw_rect(renderer,
287 &elem->corner,
288 &lr_corner,
289 &SADTBOX_FG_COLOR);
292 text_draw(box->text, renderer);
294 idfontheight = .75 * box->text->height;
295 renderer->ops->set_font(renderer, box->text->font, idfontheight);
296 pos = lr_corner;
297 pos.x -= .3 * idfontheight;
298 pos.y -= .3 * idfontheight;
299 renderer->ops->draw_string(renderer,
300 box->id,
301 &pos, ALIGN_RIGHT,
302 &box->text->color);
305 static void
306 sadtbox_update_data(Box *box, AnchorShape horiz, AnchorShape vert)
308 Element *elem = &box->element;
309 ElementBBExtras *extra = &elem->extra_spacing;
310 Object *obj = &elem->object;
311 Point center, bottom_right;
312 Point p;
313 real width, height;
314 Point nw,ne,se,sw;
316 /* save starting points */
317 center = bottom_right = elem->corner;
318 center.x += elem->width/2;
319 bottom_right.x += elem->width;
320 center.y += elem->height/2;
321 bottom_right.y += elem->height;
323 text_calc_boundingbox(box->text, NULL);
324 width = box->text->max_width + box->padding*2;
325 height = box->text->height * box->text->numlines + box->padding*2;
327 if (width > elem->width) elem->width = width;
328 if (height > elem->height) elem->height = height;
330 /* move shape if necessary ... */
331 switch (horiz) {
332 case ANCHOR_MIDDLE:
333 elem->corner.x = center.x - elem->width/2; break;
334 case ANCHOR_END:
335 elem->corner.x = bottom_right.x - elem->width; break;
336 default:
337 break;
339 switch (vert) {
340 case ANCHOR_MIDDLE:
341 elem->corner.y = center.y - elem->height/2; break;
342 case ANCHOR_END:
343 elem->corner.y = bottom_right.y - elem->height; break;
344 default:
345 break;
348 p = elem->corner;
349 p.x += elem->width / 2.0;
350 p.y += elem->height / 2.0 - box->text->height * box->text->numlines / 2 +
351 box->text->ascent;
352 text_set_position(box->text, &p);
354 extra->border_trans = SADTBOX_LINE_WIDTH / 2.0;
355 element_update_boundingbox(elem);
357 obj->position = elem->corner;
359 element_update_handles(elem);
361 /* Update connections: */
362 nw = elem->corner;
363 se = bottom_right;
364 ne.x = se.x;
365 ne.y = nw.y;
366 sw.y = se.y;
367 sw.x = nw.x;
369 connpointline_update(box->north);
370 connpointline_putonaline(box->north,&ne,&nw);
371 connpointline_update(box->west);
372 connpointline_putonaline(box->west,&nw,&sw);
373 connpointline_update(box->south);
374 connpointline_putonaline(box->south,&sw,&se);
375 connpointline_update(box->east);
376 connpointline_putonaline(box->east,&se,&ne);
380 static ConnPointLine *
381 sadtbox_get_clicked_border(Box *box, Point *clicked)
383 ConnPointLine *cpl;
384 real dist,dist2;
386 cpl = box->north;
387 dist = distance_line_point(&box->north->start,&box->north->end,0,clicked);
389 dist2 = distance_line_point(&box->west->start,&box->west->end,0,clicked);
390 if (dist2 < dist) {
391 cpl = box->west;
392 dist = dist2;
394 dist2 = distance_line_point(&box->south->start,&box->south->end,0,clicked);
395 if (dist2 < dist) {
396 cpl = box->south;
397 dist = dist2;
399 dist2 = distance_line_point(&box->east->start,&box->east->end,0,clicked);
400 if (dist2 < dist) {
401 cpl = box->east;
402 /*dist = dist2;*/
404 return cpl;
407 inline static ObjectChange *
408 sadtbox_create_change(Box *box, ObjectChange *inner, ConnPointLine *cpl) {
409 return (ObjectChange *)inner;
412 static ObjectChange *
413 sadtbox_add_connpoint_callback(Object *obj, Point *clicked, gpointer data)
415 ObjectChange *change;
416 ConnPointLine *cpl;
417 Box *box = (Box *)obj;
419 cpl = sadtbox_get_clicked_border(box,clicked);
420 change = connpointline_add_point(cpl, clicked);
421 sadtbox_update_data((Box *)obj,ANCHOR_MIDDLE, ANCHOR_MIDDLE);
422 return sadtbox_create_change(box,change,cpl);
425 static ObjectChange *
426 sadtbox_remove_connpoint_callback(Object *obj, Point *clicked, gpointer data)
428 ObjectChange *change;
429 ConnPointLine *cpl;
430 Box *box = (Box *)obj;
432 cpl = sadtbox_get_clicked_border(box,clicked);
433 change = connpointline_remove_point(cpl, clicked);
434 sadtbox_update_data((Box *)obj,ANCHOR_MIDDLE, ANCHOR_MIDDLE);
435 return sadtbox_create_change(box,change,cpl);
438 static DiaMenuItem object_menu_items[] = {
439 { N_("Add connection point"), sadtbox_add_connpoint_callback, NULL, 1 },
440 { N_("Delete connection point"), sadtbox_remove_connpoint_callback,
441 NULL, 1 },
444 static DiaMenu object_menu = {
445 N_("SADT box"),
446 sizeof(object_menu_items)/sizeof(DiaMenuItem),
447 object_menu_items,
448 NULL
451 static DiaMenu *
452 sadtbox_get_object_menu(Box *box, Point *clickedpoint)
454 ConnPointLine *cpl;
456 cpl = sadtbox_get_clicked_border(box,clickedpoint);
457 /* Set entries sensitive/selected etc here */
458 object_menu_items[0].active = connpointline_can_add_point(cpl, clickedpoint);
459 object_menu_items[1].active = connpointline_can_remove_point(cpl, clickedpoint);
460 return &object_menu;
464 static Object *
465 sadtbox_create(Point *startpoint,
466 void *user_data,
467 Handle **handle1,
468 Handle **handle2)
470 Box *box;
471 Element *elem;
472 Object *obj;
473 Point p;
474 DiaFont* font;
476 box = g_malloc0(sizeof(Box));
477 elem = &box->element;
478 obj = &elem->object;
480 obj->type = &sadtbox_type;
482 obj->ops = &sadtbox_ops;
484 elem->corner = *startpoint;
485 elem->width = DEFAULT_WIDTH;
486 elem->height = DEFAULT_HEIGHT;
488 box->padding = 0.5; /* default_values.padding; */
490 p = *startpoint;
491 p.x += elem->width / 2.0;
492 p.y += elem->height / 2.0 + /*default_properties.font_size*/ 0.8 / 2;
494 font = dia_font_new(BASIC_SANS_FONT,STYLE_BOLD,0.8);
496 box->text = new_text("", font,
497 0.8, &p,
498 &color_black,
499 ALIGN_CENTER);
500 dia_font_unref(font);
502 box->id = g_strdup("A0"); /* should be made better.
503 Automatic counting ? */
505 element_init(elem, 8, 0);
507 box->north = connpointline_create(obj,4);
508 box->west = connpointline_create(obj,3);
509 box->south = connpointline_create(obj,1);
510 box->east = connpointline_create(obj,3);
512 box->element.extra_spacing.border_trans = SADTBOX_LINE_WIDTH/2.0;
513 sadtbox_update_data(box, ANCHOR_MIDDLE, ANCHOR_MIDDLE);
515 *handle1 = NULL;
516 *handle2 = obj->handles[7];
517 return &box->element.object;
520 static void
521 sadtbox_destroy(Box *box)
523 text_destroy(box->text);
525 connpointline_destroy(box->east);
526 connpointline_destroy(box->south);
527 connpointline_destroy(box->west);
528 connpointline_destroy(box->north);
530 g_free(box->id);
532 element_destroy(&box->element);
536 static Object *
537 sadtbox_load(ObjectNode obj_node, int version, const char *filename)
539 return object_load_using_properties(&sadtbox_type,
540 obj_node,version,filename);