2002-06-23 Hans Breuer <hans@breuer.org>
[dia.git] / objects / GRAFCET / step.c
blob3a1b6cca62c4b5663d774076332554a19708be39
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * GRAFCET charts support for Dia
5 * Copyright (C) 2000, 2001 Cyrille Chepelov
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <assert.h>
27 #include <gtk/gtk.h>
28 #include <math.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #include "intl.h"
34 #include "object.h"
35 #include "element.h"
36 #include "connectionpoint.h"
37 #include "render.h"
38 #include "attributes.h"
39 #include "widgets.h"
40 #include "properties.h"
42 #include "grafcet.h"
44 #include "pixmaps/etape.xpm"
46 #define STEP_FONT BASIC_SANS_FONT
47 #define STEP_FONT_STYLE STYLE_BOLD
48 #define STEP_FONT_HEIGHT 1
49 #define STEP_LINE_WIDTH GRAFCET_GENERAL_LINE_WIDTH
50 #define STEP_WIDTH 3.0
51 #define STEP_DECLAREDWIDTH 4.0
52 #define STEP_HEIGHT 4.0
53 #define STEP_DOT_RADIUS .35
55 #define HANDLE_NORTH HANDLE_CUSTOM1
56 #define HANDLE_SOUTH HANDLE_CUSTOM2
59 typedef enum {
60 STEP_NORMAL,
61 STEP_INITIAL,
62 STEP_MACROENTRY,
63 STEP_MACROEXIT,
64 STEP_MACROCALL,
65 STEP_SUBPCALL} StepType;
67 typedef struct _Step {
68 Element element;
70 ConnectionPoint connections[4];
72 char *id;
73 int active;
74 StepType type;
76 DiaFont *font;
77 real font_size;
78 Color font_color;
80 Handle north,south;
81 Point SD1,SD2,NU1,NU2;
83 /* These are useful points for drawing.
84 Must be in sequence, A first, Z last. */
85 Point A,B,C,D,E,F,G,H,I,J,Z;
86 } Step;
88 static real step_distance_from(Step *step, Point *point);
89 static void step_select(Step *step, Point *clicked_point,
90 Renderer *interactive_renderer);
91 static void step_move_handle(Step *step, Handle *handle,
92 Point *to, HandleMoveReason reason, ModifierKeys modifiers);
93 static void step_move(Step *step, Point *to);
94 static void step_draw(Step *step, Renderer *renderer);
95 static void step_update_data(Step *step);
96 static Object *step_create(Point *startpoint,
97 void *user_data,
98 Handle **handle1,
99 Handle **handle2);
100 static void step_destroy(Step *step);
102 static void step_been_renamed(const gchar *sid);
104 static Object *step_load(ObjectNode obj_node, int version,
105 const char *filename);
106 static PropDescription *step_describe_props(Step *step);
107 static void step_get_props(Step *step,
108 GPtrArray *props);
109 static void step_set_props(Step *step,
110 GPtrArray *props);
112 static ObjectTypeOps step_type_ops =
114 (CreateFunc) step_create,
115 (LoadFunc) step_load/*using properties*/,
116 (SaveFunc) object_save_using_properties,
117 (GetDefaultsFunc) NULL,
118 (ApplyDefaultsFunc) NULL,
121 ObjectType step_type =
123 "GRAFCET - Step", /* name */
124 0, /* version */
125 (char **) etape_xpm, /* pixmap */
127 &step_type_ops /* ops */
130 static ObjectOps step_ops = {
131 (DestroyFunc) step_destroy,
132 (DrawFunc) step_draw,
133 (DistanceFunc) step_distance_from,
134 (SelectFunc) step_select,
135 (CopyFunc) object_copy_using_properties,
136 (MoveFunc) step_move,
137 (MoveHandleFunc) step_move_handle,
138 (GetPropertiesFunc) object_create_props_dialog,
139 (ApplyPropertiesFunc) object_apply_props_from_dialog,
140 (ObjectMenuFunc) NULL,
141 (DescribePropsFunc) step_describe_props,
142 (GetPropsFunc) step_get_props,
143 (SetPropsFunc) step_set_props
146 PropEnumData step_style[] = {
147 { N_("Regular step"),STEP_NORMAL },
148 { N_("Initial step"),STEP_INITIAL },
149 { N_("Macro entry step"),STEP_MACROENTRY },
150 { N_("Macro exit step"),STEP_MACROEXIT },
151 { N_("Macro call step"),STEP_MACROCALL },
152 { N_("Subprogram call step"), STEP_SUBPCALL },
153 { NULL }};
155 static PropDescription step_props[] = {
156 ELEMENT_COMMON_PROPERTIES,
157 { "id", PROP_TYPE_STRING, PROP_FLAG_VISIBLE|PROP_FLAG_DONT_MERGE,
158 N_("Step name"),N_("The name of the step")},
159 { "type", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
160 N_("Step type"),N_("The kind of step"),step_style},
161 { "active", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE,
162 N_("Active"), N_("Shows a red dot to figure the step's activity")},
163 { "font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE,
164 N_("Font"),NULL},
165 { "font_size", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
166 N_("Font size"), NULL, &prop_std_text_height_data},
167 { "font_color", PROP_TYPE_COLOUR, PROP_FLAG_VISIBLE,
168 N_("Text colour"), NULL },
169 { "north_pos", PROP_TYPE_POINT, 0},
170 { "south_pos", PROP_TYPE_POINT, 0},
171 PROP_DESC_END
174 static PropDescription *
175 step_describe_props(Step *step)
177 if (step_props[0].quark == 0) {
178 prop_desc_list_calculate_quarks(step_props);
180 return step_props;
183 static PropOffset step_offsets[] = {
184 ELEMENT_COMMON_PROPERTIES_OFFSETS,
185 { "id", PROP_TYPE_STRING, offsetof(Step,id)},
186 { "type", PROP_TYPE_ENUM, offsetof(Step,type)},
187 { "active", PROP_TYPE_BOOL, offsetof(Step,active)},
188 { "font", PROP_TYPE_FONT, offsetof(Step,font)},
189 { "font_size", PROP_TYPE_REAL, offsetof(Step,font_size)},
190 { "font_color", PROP_TYPE_COLOUR, offsetof(Step,font_color)},
191 { "north_pos", PROP_TYPE_POINT, offsetof(Step,north.pos)},
192 { "south_pos", PROP_TYPE_POINT, offsetof(Step,south.pos)},
193 { NULL,0,0 }
196 static void
197 step_get_props(Step *step, GPtrArray *props)
199 object_get_props_from_offsets(&step->element.object,
200 step_offsets,props);
203 static void
204 step_set_props(Step *step, GPtrArray *props)
206 object_set_props_from_offsets(&step->element.object,
207 step_offsets,props);
208 step_been_renamed(step->id);
209 step_update_data(step);
212 /* the following two functions try to be clever when allocating
213 step numbers */
215 static int __stepnum = 0;
216 static int __Astyle = 0;
217 static gchar *new_step_name()
219 char snum[16];
220 char *p = snum;
222 if (__Astyle) *p++ = 'A';
224 g_snprintf(p,sizeof(snum)-2,"%d",__stepnum++);
225 return g_strdup(snum);
228 static void step_been_renamed(const gchar *sid)
230 gchar *endptr;
231 long int snum;
232 if (!sid) return;
233 if (*sid == 'A') {
234 sid++; /* for the "A01" numbering style */
235 __Astyle = 1;
236 } else {
237 __Astyle = 0;
239 endptr = NULL;
240 snum = strtol(sid,&endptr,10);
241 if (*endptr == '\0') __stepnum = snum + 1;
244 static Color color_red = { 1.0f, 0.0f, 0.0f };
246 static real
247 step_distance_from(Step *step, Point *point)
249 Element *elem = &step->element;
250 Rectangle rect;
251 real dist;
253 dist = distance_line_point(&step->north.pos,&step->NU1,
254 STEP_LINE_WIDTH,point);
255 dist = MIN(dist,distance_line_point(&step->NU1,&step->NU2,
256 STEP_LINE_WIDTH,point));
257 dist = MIN(dist,distance_line_point(&step->NU2,&step->A,
258 STEP_LINE_WIDTH,point));
259 dist = MIN(dist,distance_line_point(&step->D,&step->SD1,
260 STEP_LINE_WIDTH,point));
261 dist = MIN(dist,distance_line_point(&step->SD1,&step->SD2,
262 STEP_LINE_WIDTH,point));
263 dist = MIN(dist,distance_line_point(&step->SD2,&step->south.pos,
264 STEP_LINE_WIDTH,point));
266 rect.left = elem->corner.x;
267 rect.right = elem->corner.x + elem->width;
268 rect.top = elem->corner.y;
269 rect.bottom = elem->corner.y + elem->height;
270 dist = MIN(dist,distance_rectangle_point(&rect, point));
271 return dist;
274 static void
275 step_select(Step *step, Point *clicked_point,
276 Renderer *interactive_renderer)
278 element_update_handles(&step->element);
281 static void
282 step_move_handle(Step *step, Handle *handle,
283 Point *to, HandleMoveReason reason, ModifierKeys modifiers)
285 assert(step!=NULL);
286 assert(handle!=NULL);
287 assert(to!=NULL);
289 switch(handle->id) {
290 case HANDLE_NORTH:
291 step->north.pos = *to;
292 if (step->north.pos.y > step->A.y) step->north.pos.y = step->A.y;
293 break;
294 case HANDLE_SOUTH:
295 step->south.pos = *to;
296 if (step->south.pos.y < step->D.y) step->south.pos.y = step->D.y;
297 break;
298 default:
299 element_move_handle(&step->element, handle->id, to, reason);
302 step_update_data(step);
305 static void
306 step_move(Step *step, Point *to)
308 Point delta = *to;
309 point_sub(&delta,&step->element.corner);
310 step->element.corner = *to;
311 point_add(&step->north.pos,&delta);
312 point_add(&step->south.pos,&delta);
314 step_update_data(step);
318 static void
319 step_draw(Step *step, Renderer *renderer)
321 Point pts[4];
322 assert(step != NULL);
323 assert(renderer != NULL);
325 renderer->ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
326 renderer->ops->set_linewidth(renderer, STEP_LINE_WIDTH);
327 renderer->ops->set_linestyle(renderer, LINESTYLE_SOLID);
328 renderer->ops->set_linejoin(renderer, LINEJOIN_MITER);
330 pts[0] = step->north.pos;
331 pts[1] = step->NU1;
332 pts[2] = step->NU2;
333 pts[3] = step->A;
334 renderer->ops->draw_polyline(renderer,pts,sizeof(pts)/sizeof(pts[0]),
335 &color_black);
336 pts[0] = step->D;
337 pts[1] = step->SD1;
338 pts[2] = step->SD2;
339 pts[3] = step->south.pos;
340 renderer->ops->draw_polyline(renderer,pts,sizeof(pts)/sizeof(pts[0]),
341 &color_black);
343 if ((step->type == STEP_INITIAL) ||
344 (step->type == STEP_MACROCALL) ||
345 (step->type == STEP_SUBPCALL)) {
346 renderer->ops->fill_rect(renderer, &step->I, &step->J, &color_white);
347 renderer->ops->draw_rect(renderer, &step->I, &step->J, &color_black);
348 } else {
349 renderer->ops->fill_rect(renderer, &step->E, &step->F, &color_white);
351 renderer->ops->draw_rect(renderer, &step->E, &step->F, &color_black);
353 if (step->type != STEP_MACROENTRY)
354 renderer->ops->draw_line(renderer,&step->A,&step->B,&color_black);
355 if (step->type != STEP_MACROEXIT)
356 renderer->ops->draw_line(renderer,&step->C,&step->D,&color_black);
358 renderer->ops->set_font(renderer, step->font, step->font_size);
360 renderer->ops->draw_string(renderer,
361 step->id,
362 &step->G, ALIGN_CENTER,
363 &step->font_color);
364 if (step->active)
365 renderer->ops->fill_ellipse(renderer,
366 &step->H,
367 STEP_DOT_RADIUS,STEP_DOT_RADIUS,
368 &color_red);
371 static void
372 step_update_data(Step *step)
374 Element *elem = &step->element;
375 Object *obj = &elem->object;
376 ElementBBExtras *extra = &elem->extra_spacing;
377 Point *p,ulc;
379 ulc = elem->corner;
380 ulc.x += ((STEP_DECLAREDWIDTH - STEP_WIDTH) / 2.0); /* we cheat a little */
382 step->A.x = 0.0 + (STEP_WIDTH / 2.0); step->A.y = 0.0;
383 step->D.x = 0.0 + (STEP_WIDTH / 2.0); step->D.y = 0.0 + STEP_HEIGHT;
385 step->E.x = 0.0; step->E.y = 0.5;
386 step->F.x = STEP_WIDTH; step->F.y = STEP_HEIGHT- 0.5;
389 switch(step->type) {
390 case STEP_INITIAL:
391 step->I.x = step->E.x - 2 * STEP_LINE_WIDTH;
392 step->I.y = step->E.y - 2 * STEP_LINE_WIDTH;
393 step->J.x = step->F.x + 2 * STEP_LINE_WIDTH;
394 step->J.y = step->F.y + 2 * STEP_LINE_WIDTH;
396 step->B.x = step->A.x; step->B.y = step->I.y;
397 step->C.x = step->D.x; step->C.y = step->J.y;
398 step->Z.x = step->J.x; step->Z.y = STEP_HEIGHT / 2;
399 break;
400 case STEP_MACROCALL:
401 step->I.x = step->E.x;
402 step->I.y = step->E.y - 2 * STEP_LINE_WIDTH;
403 step->J.x = step->F.x;
404 step->J.y = step->F.y + 2 * STEP_LINE_WIDTH;
406 step->B.x = step->A.x; step->B.y = step->I.y;
407 step->C.x = step->D.x; step->C.y = step->J.y;
408 step->Z.x = step->J.x; step->Z.y = STEP_HEIGHT / 2;
409 break;
410 case STEP_SUBPCALL:
411 step->I.x = step->E.x - 2 * STEP_LINE_WIDTH;
412 step->I.y = step->E.y;
413 step->J.x = step->F.x + 2 * STEP_LINE_WIDTH;
414 step->J.y = step->F.y;
416 step->B.x = step->A.x; step->B.y = step->I.y;
417 step->C.x = step->D.x; step->C.y = step->J.y;
418 step->Z.x = step->J.x; step->Z.y = STEP_HEIGHT / 2;
419 break;
420 default: /* regular or macro end steps */
421 step->B.x = step->A.x; step->B.y = step->E.y;
422 step->C.x = step->D.x; step->C.y = step->F.y;
423 step->Z.x = step->F.x; step->Z.y = STEP_HEIGHT / 2;
426 step->G.x = step->A.x;
427 step->G.y = (STEP_HEIGHT / 2) + (.3 * step->font_size);
428 step->H.x = step->E.x + (1.2 * STEP_DOT_RADIUS);
429 step->H.y = step->F.y - (1.2 * STEP_DOT_RADIUS);
431 for (p=&(step->A); p<=&(step->Z) ; p++)
432 point_add(p,&ulc);
434 /* Update handles: */
435 if (step->north.pos.x == -65536.0) {
436 step->north.pos = step->A;
437 step->south.pos = step->D;
439 step->NU1.x = step->north.pos.x;
440 step->NU2.x = step->A.x;
441 step->NU1.y = step->NU2.y = (step->north.pos.y + step->A.y) / 2.0;
442 step->SD1.x = step->D.x;
443 step->SD2.x = step->south.pos.x;
444 step->SD1.y = step->SD2.y = (step->south.pos.y + step->D.y) / 2.0;
446 /* Update connections: */
447 step->connections[0].pos = step->A;
448 step->connections[1].pos = step->D;
449 step->connections[2].pos = step->Z;
450 step->connections[3].pos = step->H;
452 /* recalc the bounding box : */
453 if ((step->type == STEP_INITIAL) || (step->type == STEP_SUBPCALL)) {
454 extra->border_trans = 2.5 * STEP_LINE_WIDTH;
455 } else {
456 extra->border_trans = STEP_LINE_WIDTH / 2;
459 element_update_boundingbox(elem);
460 rectangle_add_point(&obj->bounding_box,&step->north.pos);
461 rectangle_add_point(&obj->bounding_box,&step->south.pos);
463 obj->position = elem->corner;
465 element_update_handles(elem);
468 static Object *
469 step_create(Point *startpoint,
470 void *user_data,
471 Handle **handle1,
472 Handle **handle2)
474 Step *step;
475 Element *elem;
476 Object *obj;
477 int i;
478 int type;
480 step = g_new0(Step,1);
481 elem = &step->element;
482 obj = &elem->object;
484 obj->type = &step_type;
485 obj->ops = &step_ops;
487 elem->corner = *startpoint;
488 elem->width = STEP_DECLAREDWIDTH;
489 elem->height = STEP_HEIGHT;
491 element_init(elem, 10, 4);
493 for (i=0;i<4;i++) {
494 obj->connections[i] = &step->connections[i];
495 step->connections[i].object = obj;
496 step->connections[i].connected = NULL;
499 step->id = new_step_name();
500 step->active = 0;
501 step->font = dia_font_new (STEP_FONT,STEP_FONT_STYLE,STEP_FONT_HEIGHT);
502 step->font_size = STEP_FONT_HEIGHT;
503 step->font_color = color_black;
505 type = GPOINTER_TO_INT(user_data);
506 switch(type) {
507 case STEP_NORMAL:
508 case STEP_INITIAL:
509 case STEP_MACROENTRY:
510 case STEP_MACROEXIT:
511 case STEP_MACROCALL:
512 case STEP_SUBPCALL:
513 step->type = type;
514 break;
515 default:
516 step->type = STEP_NORMAL;
520 for (i=0;i<8;i++) {
521 obj->handles[i]->type = HANDLE_NON_MOVABLE;
523 obj->handles[8] = &step->north;
524 obj->handles[9] = &step->south;
525 step->north.connect_type = HANDLE_CONNECTABLE;
526 step->north.type = HANDLE_MAJOR_CONTROL;
527 step->north.id = HANDLE_NORTH;
528 step->south.connect_type = HANDLE_CONNECTABLE;
529 step->south.type = HANDLE_MAJOR_CONTROL;
530 step->south.id = HANDLE_SOUTH;
531 step->north.pos.x = -65536.0; /* magic */
533 step_update_data(step);
535 *handle1 = NULL;
536 *handle2 = obj->handles[0];
537 return &step->element.object;
540 static void
541 step_destroy(Step *step)
543 dia_font_unref(step->font);
544 g_free(step->id);
545 element_destroy(&step->element);
548 static Object *
549 step_load(ObjectNode obj_node, int version, const char *filename)
551 return object_load_using_properties(&step_type,
552 obj_node,version,filename);