1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 *
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)
7 *
8 * based on SADT diagrams copyright (C) 2000, 2001 Cyrille Chepelov
9 *
10 * Forked from Flowchart toolbox -- objects for drawing flowcharts.
11 * Copyright (C) 1999 James Henstridge.
12 *
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.
17 *
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.
22 *
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.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <assert.h>
33 #include <math.h>
34 #include <string.h>
35
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"
43
44 #include "pixmaps/requirement.xpm"
45
46 typedef struct _RequirementPropertiesDialog RequirementPropertiesDialog;
47 typedef struct _Requirement Requirement;
48 typedef struct _RequirementState RequirementState;
49
50 #define NUM_CONNECTIONS 9
51
52 struct _Requirement {
53 Element element;
54
55 ConnectionPoint connections[NUM_CONNECTIONS];
56
57 Text *text;
58 int text_outside;
59 int collaboration;
60 TextAttributes attrs;
61
62 int init;
63 };
64
65
66 struct _RequirementPropertiesDialog {
67 GtkWidget *dialog;
68
69 GtkToggleButton *text_out;
70 GtkToggleButton *collaboration;
71 };
72
73
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
82
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);
102
103 static ObjectTypeOps req_type_ops =
104 {
105 (CreateFunc) req_create,
106 (LoadFunc) req_load,/*using_properties*/ /* load */
107 (SaveFunc) object_save_using_properties, /* save */
108 (GetDefaultsFunc) NULL,
109 (ApplyDefaultsFunc) NULL
110 };
111
112 DiaObjectType jackson_requirement_type =
113 {
114 "Jackson - requirement", /* name */
115 0, /* version */
116 (char **)jackson_requirement_xpm, /* pixmap */
117
118 &req_type_ops /* ops */
119 };
120
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 (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
131 (ObjectMenuFunc) NULL,
132 (DescribePropsFunc) req_describe_props,
133 (GetPropsFunc) req_get_props,
134 (SetPropsFunc) req_set_props,
135 (TextEditFunc) 0,
136 (ApplyPropertiesListFunc) object_apply_props,
137 };
138
139 static PropDescription req_props[] = {
140 ELEMENT_COMMON_PROPERTIES,
141 PROP_STD_TEXT_FONT,
142 PROP_STD_TEXT_HEIGHT,
143 PROP_STD_TEXT_COLOUR,
144 { "text", PROP_TYPE_TEXT, 0, N_("Text"), NULL, NULL },
145
146 PROP_DESC_END
147 };
148
149 static PropDescription *
req_describe_props(Requirement * req)150 req_describe_props(Requirement *req)
151 {
152 return req_props;
153 }
154
155 static PropOffset req_offsets[] = {
156 ELEMENT_COMMON_PROPERTIES_OFFSETS,
157 {"text",PROP_TYPE_TEXT,offsetof(Requirement,text)},
158 {"text_font",PROP_TYPE_FONT,offsetof(Requirement,attrs.font)},
159 {PROP_STDNAME_TEXT_HEIGHT,PROP_STDTYPE_TEXT_HEIGHT,offsetof(Requirement,attrs.height)},
160 {"text_colour",PROP_TYPE_COLOUR,offsetof(Requirement,attrs.color)},
161 { NULL, 0, 0 },
162 };
163
164 static void
req_get_props(Requirement * req,GPtrArray * props)165 req_get_props(Requirement * req, GPtrArray *props)
166 {
167 text_get_attributes(req->text,&req->attrs);
168 object_get_props_from_offsets(&req->element.object,
169 req_offsets,props);
170 }
171
172 static void
req_set_props(Requirement * req,GPtrArray * props)173 req_set_props(Requirement *req, GPtrArray *props)
174 {
175 if (req->init==-1) { req->init++; return; } /* workaround init bug */
176
177 object_set_props_from_offsets(&req->element.object,
178 req_offsets,props);
179 apply_textattr_properties(props,req->text,"text",&req->attrs);
180 req_update_data(req);
181 }
182
183 static real
req_distance_from(Requirement * req,Point * point)184 req_distance_from(Requirement *req, Point *point)
185 {
186 DiaObject *obj = &req->element.object;
187 return distance_rectangle_point(&obj->bounding_box, point);
188 }
189
190 static void
req_select(Requirement * req,Point * clicked_point,DiaRenderer * interactive_renderer)191 req_select(Requirement *req, Point *clicked_point,
192 DiaRenderer *interactive_renderer)
193 {
194 text_set_cursor(req->text, clicked_point, interactive_renderer);
195 text_grab_focus(req->text, &req->element.object);
196 element_update_handles(&req->element);
197 }
198
199 static ObjectChange*
req_move_handle(Requirement * req,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)200 req_move_handle(Requirement *req, Handle *handle,
201 Point *to, ConnectionPoint *cp,
202 HandleMoveReason reason, ModifierKeys modifiers)
203 {
204 assert(req!=NULL);
205 assert(handle!=NULL);
206 assert(to!=NULL);
207
208 assert(handle->id < 8);
209 return NULL;
210 }
211
212 static ObjectChange*
req_move(Requirement * req,Point * to)213 req_move(Requirement *req, Point *to)
214 {
215 real h;
216 Point p;
217
218 req->element.corner = *to;
219 h = req->text->height*req->text->numlines;
220
221 p = *to;
222 p.x += req->element.width/2.0;
223 if (req->text_outside) {
224 p.y += req->element.height - h + req->text->ascent;
225 } else {
226 p.y += (req->element.height - h)/2.0 + req->text->ascent;
227 }
228 text_set_position(req->text, &p);
229 req_update_data(req);
230 return NULL;
231 }
232
233 /** draw is here */
234 static void
req_draw(Requirement * req,DiaRenderer * renderer)235 req_draw(Requirement *req, DiaRenderer *renderer)
236 {
237 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
238 Element *elem;
239 real x, y, w, h;
240 Point c;
241
242 assert(req != NULL);
243 assert(renderer != NULL);
244
245 elem = &req->element;
246
247 x = elem->corner.x;
248 y = elem->corner.y;
249
250 /*
251 if (req->text_outside) {
252 w = REQ_WIDTH;
253 h = REQ_HEIGHT;
254 c.x = x + elem->width/2.0;
255 c.y = y + REQ_HEIGHT / 2.0;
256 } else {
257 */
258
259 w = elem->width;
260 h = elem->height;
261 c.x = x + w/2.0;
262 c.y = y + h/2.0;
263
264 renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
265 renderer_ops->set_linewidth(renderer, REQ_LINEWIDTH);
266
267 renderer_ops->set_dashlength(renderer, REQ_DASHLEN);
268 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
269
270 renderer_ops->fill_ellipse(renderer, &c, w, h, &color_white);
271 renderer_ops->draw_ellipse(renderer, &c, w, h, &color_black);
272
273 text_draw(req->text, renderer);
274 }
275
276
277 static void
req_update_data(Requirement * req)278 req_update_data(Requirement *req)
279 {
280 real w, h, ratio;
281 Point c, half, r,p;
282
283 Element *elem = &req->element;
284 DiaObject *obj = &elem->object;
285
286 text_calc_boundingbox(req->text, NULL);
287 w = req->text->max_width;
288 h = req->text->height*req->text->numlines;
289
290 if (!req->text_outside) {
291 ratio = w/h;
292
293 if (ratio > REQ_MAX_RATIO)
294 ratio = REQ_MAX_RATIO;
295
296 if (ratio < REQ_MIN_RATIO) {
297 ratio = REQ_MIN_RATIO;
298 r.y = w / ratio + h;
299 r.x = r.y * ratio;
300 } else {
301 r.x = ratio*h + w;
302 r.y = r.x / ratio;
303 }
304 if (r.x < REQ_WIDTH)
305 r.x = REQ_WIDTH;
306 if (r.y < REQ_HEIGHT)
307 r.y = REQ_HEIGHT;
308 } else {
309 r.x = REQ_WIDTH;
310 r.y = REQ_HEIGHT;
311 }
312
313 elem->width = r.x;
314 elem->height = r.y;
315
316 if (req->text_outside) {
317 elem->width = MAX(elem->width, w);
318 elem->height += h + REQ_MARGIN_Y;
319 }
320
321 r.x /= 2.0;
322 r.y /= 2.0;
323 c.x = elem->corner.x + elem->width / 2.0;
324 c.y = elem->corner.y + r.y;
325 half.x = r.x * M_SQRT1_2;
326 half.y = r.y * M_SQRT1_2;
327
328 /* Update connections: */
329 req->connections[0].pos.x = c.x - half.x;
330 req->connections[0].pos.y = c.y - half.y;
331 req->connections[1].pos.x = c.x;
332 req->connections[1].pos.y = elem->corner.y;
333 req->connections[2].pos.x = c.x + half.x;
334 req->connections[2].pos.y = c.y - half.y;
335 req->connections[3].pos.x = c.x - r.x;
336 req->connections[3].pos.y = c.y;
337 req->connections[4].pos.x = c.x + r.x;
338 req->connections[4].pos.y = c.y;
339
340 if (req->text_outside) {
341 req->connections[5].pos.x = elem->corner.x;
342 req->connections[5].pos.y = elem->corner.y + elem->height;
343 req->connections[6].pos.x = c.x;
344 req->connections[6].pos.y = elem->corner.y + elem->height;
345 req->connections[7].pos.x = elem->corner.x + elem->width;
346 req->connections[7].pos.y = elem->corner.y + elem->height;
347 } else {
348 req->connections[5].pos.x = c.x - half.x;
349 req->connections[5].pos.y = c.y + half.y;
350 req->connections[6].pos.x = c.x;
351 req->connections[6].pos.y = elem->corner.y + elem->height;
352 req->connections[7].pos.x = c.x + half.x;
353 req->connections[7].pos.y = c.y + half.y;
354 }
355 req->connections[8].pos.x = elem->corner.x + elem->width/2;
356 req->connections[8].pos.y = elem->corner.y + elem->height/2;
357
358 h = req->text->height*req->text->numlines;
359 p = req->element.corner;
360 p.x += req->element.width/2.0;
361 if (req->text_outside) {
362 p.y += req->element.height - h + req->text->ascent;
363 } else {
364 p.y += (req->element.height - h)/2.0 + req->text->ascent;
365 }
366 text_set_position(req->text, &p);
367
368 element_update_boundingbox(elem);
369
370 obj->position = elem->corner;
371
372 element_update_handles(elem);
373
374 }
375
376 /** creation here */
377 static DiaObject *
req_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)378 req_create(Point *startpoint,
379 void *user_data,
380 Handle **handle1,
381 Handle **handle2)
382 {
383 Requirement *req;
384 Element *elem;
385 DiaObject *obj;
386 Point p;
387 DiaFont *font;
388 int i;
389
390 req = g_malloc0(sizeof(Requirement));
391 elem = &req->element;
392 obj = &elem->object;
393
394 obj->type = &jackson_requirement_type;
395 obj->ops = &req_ops;
396
397 elem->corner = *startpoint;
398 elem->width = REQ_WIDTH;
399 elem->height = REQ_HEIGHT;
400
401 font = dia_font_new_from_style(DIA_FONT_SANS, REQ_FONT);
402 p = *startpoint;
403 p.x += REQ_WIDTH/2.0;
404 p.y += REQ_HEIGHT/2.0;
405
406 req->text = new_text("", font, REQ_FONT, &p, &color_black, ALIGN_CENTER);
407 dia_font_unref(font);
408 text_get_attributes(req->text,&req->attrs);
409 req->text_outside = 0;
410 req->collaboration = 0;
411 element_init(elem, 8, NUM_CONNECTIONS);
412
413 for (i=0;i<NUM_CONNECTIONS;i++) {
414 obj->connections[i] = &req->connections[i];
415 req->connections[i].object = obj;
416 req->connections[i].connected = NULL;
417 }
418 req->connections[8].flags = CP_FLAGS_MAIN;
419 elem->extra_spacing.border_trans = 0.0;
420 req_update_data(req);
421
422 for (i=0;i<8;i++) {
423 obj->handles[i]->type = HANDLE_NON_MOVABLE;
424 }
425
426 *handle1 = NULL;
427 *handle2 = NULL;
428
429 if (GPOINTER_TO_INT(user_data)!=0) req->init=-1; else req->init=0;
430
431 return &req->element.object;
432 }
433
434 static void
req_destroy(Requirement * req)435 req_destroy(Requirement *req)
436 {
437 text_destroy(req->text);
438
439 element_destroy(&req->element);
440 }
441
442 static DiaObject *
req_load(ObjectNode obj_node,int version,const char * filename)443 req_load(ObjectNode obj_node, int version, const char *filename)
444 {
445 return object_load_using_properties(&jackson_requirement_type,
446 obj_node,version,filename);
447 }
448
449
450
451