2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / Jackson / requirement.c
blobc504542a5fa2a931b17e1510173a453e82efa214
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 <assert.h>
33 #include <math.h>
34 #include <string.h>
36 #include "intl.h"
37 #include "object.h"
38 #include "element.h"
39 #include "diarenderer.h"
40 #include "attributes.h"
41 #include "text.h"
42 #include "properties.h"
44 #include "pixmaps/requirement.xpm"
46 typedef struct _RequirementPropertiesDialog RequirementPropertiesDialog;
47 typedef struct _Requirement Requirement;
48 typedef struct _RequirementState RequirementState;
50 #define NUM_CONNECTIONS 9
52 struct _Requirement {
53 Element element;
55 ConnectionPoint connections[NUM_CONNECTIONS];
57 Text *text;
58 int text_outside;
59 int collaboration;
60 TextAttributes attrs;
62 int init;
66 struct _RequirementPropertiesDialog {
67 GtkWidget *dialog;
69 GtkToggleButton *text_out;
70 GtkToggleButton *collaboration;
74 #define REQ_FONT 0.7
75 #define REQ_WIDTH 3.25
76 #define REQ_HEIGHT 2
77 #define REQ_MIN_RATIO 1.5
78 #define REQ_MAX_RATIO 3
79 #define REQ_LINEWIDTH 0.09
80 #define REQ_MARGIN_Y 0.3
81 #define REQ_DASHLEN 0.5
83 static real req_distance_from(Requirement *req, Point *point);
84 static void req_select(Requirement *req, Point *clicked_point,
85 DiaRenderer *interactive_renderer);
86 static ObjectChange* req_move_handle(Requirement *req, Handle *handle,
87 Point *to, ConnectionPoint *cp,
88 HandleMoveReason reason, ModifierKeys modifiers);
89 static ObjectChange* req_move(Requirement *req, Point *to);
90 static void req_draw(Requirement *req, DiaRenderer *renderer);
91 static DiaObject *req_create(Point *startpoint,
92 void *user_data,
93 Handle **handle1,
94 Handle **handle2);
95 static void req_destroy(Requirement *req);
96 static DiaObject *req_load(ObjectNode obj_node, int version,
97 const char *filename);
98 static void req_update_data(Requirement *req);
99 static PropDescription *req_describe_props(Requirement *req);
100 static void req_get_props(Requirement *req, GPtrArray *props);
101 static void req_set_props(Requirement *req, GPtrArray *props);
103 static ObjectTypeOps req_type_ops =
105 (CreateFunc) req_create,
106 (LoadFunc) req_load,/*using_properties*/ /* load */
107 (SaveFunc) object_save_using_properties, /* save */
108 (GetDefaultsFunc) NULL,
109 (ApplyDefaultsFunc) NULL
112 DiaObjectType jackson_requirement_type =
114 "Jackson - requirement", /* name */
115 0, /* version */
116 (char **)jackson_requirement_xpm, /* pixmap */
118 &req_type_ops /* ops */
121 static ObjectOps req_ops = {
122 (DestroyFunc) req_destroy,
123 (DrawFunc) req_draw,
124 (DistanceFunc) req_distance_from,
125 (SelectFunc) req_select,
126 (CopyFunc) object_copy_using_properties,
127 (MoveFunc) req_move,
128 (MoveHandleFunc) req_move_handle,
129 (GetPropertiesFunc) object_create_props_dialog,
130 (ApplyPropertiesFunc) object_apply_props_from_dialog,
131 (ObjectMenuFunc) NULL,
132 (DescribePropsFunc) req_describe_props,
133 (GetPropsFunc) req_get_props,
134 (SetPropsFunc) req_set_props
137 static PropDescription req_props[] = {
138 ELEMENT_COMMON_PROPERTIES,
139 PROP_STD_TEXT_FONT,
140 PROP_STD_TEXT_HEIGHT,
141 PROP_STD_TEXT_COLOUR,
142 { "text", PROP_TYPE_TEXT, 0, N_("Text"), NULL, NULL },
144 PROP_DESC_END
147 static PropDescription *
148 req_describe_props(Requirement *req)
150 return req_props;
153 static PropOffset req_offsets[] = {
154 ELEMENT_COMMON_PROPERTIES_OFFSETS,
155 {"text",PROP_TYPE_TEXT,offsetof(Requirement,text)},
156 {"text_font",PROP_TYPE_FONT,offsetof(Requirement,attrs.font)},
157 {"text_height",PROP_TYPE_REAL,offsetof(Requirement,attrs.height)},
158 {"text_colour",PROP_TYPE_COLOUR,offsetof(Requirement,attrs.color)},
159 { NULL, 0, 0 },
162 static void
163 req_get_props(Requirement * req, GPtrArray *props)
165 text_get_attributes(req->text,&req->attrs);
166 object_get_props_from_offsets(&req->element.object,
167 req_offsets,props);
170 static void
171 req_set_props(Requirement *req, GPtrArray *props)
173 if (req->init==-1) { req->init++; return; } /* workaround init bug */
175 object_set_props_from_offsets(&req->element.object,
176 req_offsets,props);
177 apply_textattr_properties(props,req->text,"text",&req->attrs);
178 req_update_data(req);
181 static real
182 req_distance_from(Requirement *req, Point *point)
184 DiaObject *obj = &req->element.object;
185 return distance_rectangle_point(&obj->bounding_box, point);
188 static void
189 req_select(Requirement *req, Point *clicked_point,
190 DiaRenderer *interactive_renderer)
192 text_set_cursor(req->text, clicked_point, interactive_renderer);
193 text_grab_focus(req->text, &req->element.object);
194 element_update_handles(&req->element);
197 static ObjectChange*
198 req_move_handle(Requirement *req, Handle *handle,
199 Point *to, ConnectionPoint *cp,
200 HandleMoveReason reason, ModifierKeys modifiers)
202 assert(req!=NULL);
203 assert(handle!=NULL);
204 assert(to!=NULL);
206 assert(handle->id < 8);
207 return NULL;
210 static ObjectChange*
211 req_move(Requirement *req, Point *to)
213 real h;
214 Point p;
216 req->element.corner = *to;
217 h = req->text->height*req->text->numlines;
219 p = *to;
220 p.x += req->element.width/2.0;
221 if (req->text_outside) {
222 p.y += req->element.height - h + req->text->ascent;
223 } else {
224 p.y += (req->element.height - h)/2.0 + req->text->ascent;
226 text_set_position(req->text, &p);
227 req_update_data(req);
228 return NULL;
231 /** draw is here */
232 static void
233 req_draw(Requirement *req, DiaRenderer *renderer)
235 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
236 Element *elem;
237 real x, y, w, h;
238 Point c;
240 assert(req != NULL);
241 assert(renderer != NULL);
243 elem = &req->element;
245 x = elem->corner.x;
246 y = elem->corner.y;
249 if (req->text_outside) {
250 w = REQ_WIDTH;
251 h = REQ_HEIGHT;
252 c.x = x + elem->width/2.0;
253 c.y = y + REQ_HEIGHT / 2.0;
254 } else {
257 w = elem->width;
258 h = elem->height;
259 c.x = x + w/2.0;
260 c.y = y + h/2.0;
262 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
263 renderer_ops->set_linewidth(renderer, REQ_LINEWIDTH);
265 renderer_ops->set_dashlength(renderer, REQ_DASHLEN);
266 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
268 renderer_ops->fill_ellipse(renderer, &c, w, h, &color_white);
269 renderer_ops->draw_ellipse(renderer, &c, w, h, &color_black);
271 text_draw(req->text, renderer);
275 static void
276 req_update_data(Requirement *req)
278 real w, h, ratio;
279 Point c, half, r,p;
281 Element *elem = &req->element;
282 DiaObject *obj = &elem->object;
284 text_calc_boundingbox(req->text, NULL);
285 w = req->text->max_width;
286 h = req->text->height*req->text->numlines;
288 if (!req->text_outside) {
289 ratio = w/h;
291 if (ratio > REQ_MAX_RATIO)
292 ratio = REQ_MAX_RATIO;
294 if (ratio < REQ_MIN_RATIO) {
295 ratio = REQ_MIN_RATIO;
296 r.y = w / ratio + h;
297 r.x = r.y * ratio;
298 } else {
299 r.x = ratio*h + w;
300 r.y = r.x / ratio;
302 if (r.x < REQ_WIDTH)
303 r.x = REQ_WIDTH;
304 if (r.y < REQ_HEIGHT)
305 r.y = REQ_HEIGHT;
306 } else {
307 r.x = REQ_WIDTH;
308 r.y = REQ_HEIGHT;
311 elem->width = r.x;
312 elem->height = r.y;
314 if (req->text_outside) {
315 elem->width = MAX(elem->width, w);
316 elem->height += h + REQ_MARGIN_Y;
319 r.x /= 2.0;
320 r.y /= 2.0;
321 c.x = elem->corner.x + elem->width / 2.0;
322 c.y = elem->corner.y + r.y;
323 half.x = r.x * M_SQRT1_2;
324 half.y = r.y * M_SQRT1_2;
326 /* Update connections: */
327 req->connections[0].pos.x = c.x - half.x;
328 req->connections[0].pos.y = c.y - half.y;
329 req->connections[1].pos.x = c.x;
330 req->connections[1].pos.y = elem->corner.y;
331 req->connections[2].pos.x = c.x + half.x;
332 req->connections[2].pos.y = c.y - half.y;
333 req->connections[3].pos.x = c.x - r.x;
334 req->connections[3].pos.y = c.y;
335 req->connections[4].pos.x = c.x + r.x;
336 req->connections[4].pos.y = c.y;
338 if (req->text_outside) {
339 req->connections[5].pos.x = elem->corner.x;
340 req->connections[5].pos.y = elem->corner.y + elem->height;
341 req->connections[6].pos.x = c.x;
342 req->connections[6].pos.y = elem->corner.y + elem->height;
343 req->connections[7].pos.x = elem->corner.x + elem->width;
344 req->connections[7].pos.y = elem->corner.y + elem->height;
345 } else {
346 req->connections[5].pos.x = c.x - half.x;
347 req->connections[5].pos.y = c.y + half.y;
348 req->connections[6].pos.x = c.x;
349 req->connections[6].pos.y = elem->corner.y + elem->height;
350 req->connections[7].pos.x = c.x + half.x;
351 req->connections[7].pos.y = c.y + half.y;
353 req->connections[8].pos.x = elem->corner.x + elem->width/2;
354 req->connections[8].pos.y = elem->corner.y + elem->height/2;
356 h = req->text->height*req->text->numlines;
357 p = req->element.corner;
358 p.x += req->element.width/2.0;
359 if (req->text_outside) {
360 p.y += req->element.height - h + req->text->ascent;
361 } else {
362 p.y += (req->element.height - h)/2.0 + req->text->ascent;
364 text_set_position(req->text, &p);
366 element_update_boundingbox(elem);
368 obj->position = elem->corner;
370 element_update_handles(elem);
374 /** creation here */
375 static DiaObject *
376 req_create(Point *startpoint,
377 void *user_data,
378 Handle **handle1,
379 Handle **handle2)
381 Requirement *req;
382 Element *elem;
383 DiaObject *obj;
384 Point p;
385 DiaFont *font;
386 int i;
388 req = g_malloc0(sizeof(Requirement));
389 elem = &req->element;
390 obj = &elem->object;
392 obj->type = &jackson_requirement_type;
393 obj->ops = &req_ops;
395 elem->corner = *startpoint;
396 elem->width = REQ_WIDTH;
397 elem->height = REQ_HEIGHT;
399 font = dia_font_new_from_style(DIA_FONT_SANS, REQ_FONT);
400 p = *startpoint;
401 p.x += REQ_WIDTH/2.0;
402 p.y += REQ_HEIGHT/2.0;
404 req->text = new_text("", font, REQ_FONT, &p, &color_black, ALIGN_CENTER);
405 dia_font_unref(font);
406 text_get_attributes(req->text,&req->attrs);
407 req->text_outside = 0;
408 req->collaboration = 0;
409 element_init(elem, 8, NUM_CONNECTIONS);
411 for (i=0;i<NUM_CONNECTIONS;i++) {
412 obj->connections[i] = &req->connections[i];
413 req->connections[i].object = obj;
414 req->connections[i].connected = NULL;
416 req->connections[8].flags = CP_FLAGS_MAIN;
417 elem->extra_spacing.border_trans = 0.0;
418 req_update_data(req);
420 for (i=0;i<8;i++) {
421 obj->handles[i]->type = HANDLE_NON_MOVABLE;
424 *handle1 = NULL;
425 *handle2 = NULL;
427 if (GPOINTER_TO_INT(user_data)!=0) req->init=-1; else req->init=0;
429 return &req->element.object;
432 static void
433 req_destroy(Requirement *req)
435 text_destroy(req->text);
437 element_destroy(&req->element);
440 static DiaObject *
441 req_load(ObjectNode obj_node, int version, const char *filename)
443 return object_load_using_properties(&jackson_requirement_type,
444 obj_node,version,filename);