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