/* Dia -- an diagram creation/manipulation program * Copyright (C) 1998 Alexander Larsson * * GRAFCET charts support for Dia * Copyright (C) 2000, 2001 Cyrille Chepelov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "intl.h" #include "object.h" #include "element.h" #include "connectionpoint.h" #include "diarenderer.h" #include "attributes.h" #include "widgets.h" #include "properties.h" #include "grafcet.h" #include "pixmaps/etape.xpm" #define STEP_FONT (DIA_FONT_SANS|DIA_FONT_BOLD) #define STEP_FONT_HEIGHT 1 #define STEP_LINE_WIDTH GRAFCET_GENERAL_LINE_WIDTH #define STEP_WIDTH 3.0 #define STEP_DECLAREDWIDTH 4.0 #define STEP_HEIGHT 4.0 #define STEP_DOT_RADIUS .35 #define HANDLE_NORTH HANDLE_CUSTOM1 #define HANDLE_SOUTH HANDLE_CUSTOM2 typedef enum { STEP_NORMAL, STEP_INITIAL, STEP_MACROENTRY, STEP_MACROEXIT, STEP_MACROCALL, STEP_SUBPCALL} StepType; typedef struct _Step { Element element; ConnectionPoint connections[4]; char *id; int active; StepType type; DiaFont *font; real font_size; Color font_color; Handle north,south; Point SD1,SD2,NU1,NU2; /* These are useful points for drawing. Must be in sequence, A first, Z last. */ Point A,B,C,D,E,F,G,H,I,J,Z; } Step; static real step_distance_from(Step *step, Point *point); static void step_select(Step *step, Point *clicked_point, DiaRenderer *interactive_renderer); static ObjectChange* step_move_handle(Step *step, Handle *handle, Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers); static ObjectChange* step_move(Step *step, Point *to); static void step_draw(Step *step, DiaRenderer *renderer); static void step_update_data(Step *step); static DiaObject *step_create(Point *startpoint, void *user_data, Handle **handle1, Handle **handle2); static void step_destroy(Step *step); static void step_been_renamed(const gchar *sid); static DiaObject *step_load(ObjectNode obj_node, int version, const char *filename); static PropDescription *step_describe_props(Step *step); static void step_get_props(Step *step, GPtrArray *props); static void step_set_props(Step *step, GPtrArray *props); static ObjectTypeOps step_type_ops = { (CreateFunc) step_create, (LoadFunc) step_load/*using properties*/, (SaveFunc) object_save_using_properties, (GetDefaultsFunc) NULL, (ApplyDefaultsFunc) NULL, }; DiaObjectType step_type = { "GRAFCET - Step", /* name */ 0, /* version */ (char **) etape_xpm, /* pixmap */ &step_type_ops /* ops */ }; static ObjectOps step_ops = { (DestroyFunc) step_destroy, (DrawFunc) step_draw, (DistanceFunc) step_distance_from, (SelectFunc) step_select, (CopyFunc) object_copy_using_properties, (MoveFunc) step_move, (MoveHandleFunc) step_move_handle, (GetPropertiesFunc) object_create_props_dialog, (ApplyPropertiesFunc) object_apply_props_from_dialog, (ObjectMenuFunc) NULL, (DescribePropsFunc) step_describe_props, (GetPropsFunc) step_get_props, (SetPropsFunc) step_set_props }; PropEnumData step_style[] = { { N_("Regular step"),STEP_NORMAL }, { N_("Initial step"),STEP_INITIAL }, { N_("Macro entry step"),STEP_MACROENTRY }, { N_("Macro exit step"),STEP_MACROEXIT }, { N_("Macro call step"),STEP_MACROCALL }, { N_("Subprogram call step"), STEP_SUBPCALL }, { NULL }}; static PropDescription step_props[] = { ELEMENT_COMMON_PROPERTIES, { "id", PROP_TYPE_STRING, PROP_FLAG_VISIBLE|PROP_FLAG_NO_DEFAULTS|PROP_FLAG_DONT_MERGE, N_("Step name"),N_("The name of the step")}, { "type", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE|PROP_FLAG_NO_DEFAULTS|PROP_FLAG_DONT_MERGE, N_("Step type"),N_("The kind of step"),step_style}, { "active", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE, N_("Active"), N_("Shows a red dot to figure the step's activity")}, { "font", PROP_TYPE_FONT, PROP_FLAG_VISIBLE, N_("Font"),NULL}, { "font_size", PROP_TYPE_REAL, PROP_FLAG_VISIBLE, N_("Font size"), NULL, &prop_std_text_height_data}, { "font_color", PROP_TYPE_COLOUR, PROP_FLAG_VISIBLE, N_("Text color"), NULL }, { "north_pos", PROP_TYPE_POINT, 0}, { "south_pos", PROP_TYPE_POINT, 0}, PROP_DESC_END }; static PropDescription * step_describe_props(Step *step) { if (step_props[0].quark == 0) { prop_desc_list_calculate_quarks(step_props); } return step_props; } static PropOffset step_offsets[] = { ELEMENT_COMMON_PROPERTIES_OFFSETS, { "id", PROP_TYPE_STRING, offsetof(Step,id)}, { "type", PROP_TYPE_ENUM, offsetof(Step,type)}, { "active", PROP_TYPE_BOOL, offsetof(Step,active)}, { "font", PROP_TYPE_FONT, offsetof(Step,font)}, { "font_size", PROP_TYPE_REAL, offsetof(Step,font_size)}, { "font_color", PROP_TYPE_COLOUR, offsetof(Step,font_color)}, { "north_pos", PROP_TYPE_POINT, offsetof(Step,north.pos)}, { "south_pos", PROP_TYPE_POINT, offsetof(Step,south.pos)}, { NULL,0,0 } }; static void step_get_props(Step *step, GPtrArray *props) { object_get_props_from_offsets(&step->element.object, step_offsets,props); } static void step_set_props(Step *step, GPtrArray *props) { object_set_props_from_offsets(&step->element.object, step_offsets,props); step_been_renamed(step->id); step_update_data(step); } /* the following two functions try to be clever when allocating step numbers */ static int __stepnum = 0; static int __Astyle = 0; static gchar *new_step_name() { char snum[16]; char *p = snum; if (__Astyle) *p++ = 'A'; g_snprintf(p,sizeof(snum)-2,"%d",__stepnum++); return g_strdup(snum); } static void step_been_renamed(const gchar *sid) { gchar *endptr; long int snum; if (!sid) return; if (*sid == 'A') { sid++; /* for the "A01" numbering style */ __Astyle = 1; } else { __Astyle = 0; } endptr = NULL; snum = strtol(sid,&endptr,10); if (*endptr == '\0') __stepnum = snum + 1; } static Color color_red = { 1.0f, 0.0f, 0.0f }; static real step_distance_from(Step *step, Point *point) { Element *elem = &step->element; Rectangle rect; real dist; dist = distance_line_point(&step->north.pos,&step->NU1, STEP_LINE_WIDTH,point); dist = MIN(dist,distance_line_point(&step->NU1,&step->NU2, STEP_LINE_WIDTH,point)); dist = MIN(dist,distance_line_point(&step->NU2,&step->A, STEP_LINE_WIDTH,point)); dist = MIN(dist,distance_line_point(&step->D,&step->SD1, STEP_LINE_WIDTH,point)); dist = MIN(dist,distance_line_point(&step->SD1,&step->SD2, STEP_LINE_WIDTH,point)); dist = MIN(dist,distance_line_point(&step->SD2,&step->south.pos, STEP_LINE_WIDTH,point)); rect.left = elem->corner.x; rect.right = elem->corner.x + elem->width; rect.top = elem->corner.y; rect.bottom = elem->corner.y + elem->height; dist = MIN(dist,distance_rectangle_point(&rect, point)); return dist; } static void step_select(Step *step, Point *clicked_point, DiaRenderer *interactive_renderer) { element_update_handles(&step->element); } static ObjectChange* step_move_handle(Step *step, Handle *handle, Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers) { assert(step!=NULL); assert(handle!=NULL); assert(to!=NULL); switch(handle->id) { case HANDLE_NORTH: step->north.pos = *to; if (step->north.pos.y > step->A.y) step->north.pos.y = step->A.y; break; case HANDLE_SOUTH: step->south.pos = *to; if (step->south.pos.y < step->D.y) step->south.pos.y = step->D.y; break; default: element_move_handle(&step->element, handle->id, to, cp, reason, modifiers); } step_update_data(step); return NULL; } static ObjectChange* step_move(Step *step, Point *to) { Point delta = *to; point_sub(&delta,&step->element.corner); step->element.corner = *to; point_add(&step->north.pos,&delta); point_add(&step->south.pos,&delta); step_update_data(step); return NULL; } static void step_draw(Step *step, DiaRenderer *renderer) { DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer); Point pts[4]; assert(step != NULL); assert(renderer != NULL); renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID); renderer_ops->set_linewidth(renderer, STEP_LINE_WIDTH); renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID); renderer_ops->set_linejoin(renderer, LINEJOIN_MITER); pts[0] = step->north.pos; pts[1] = step->NU1; pts[2] = step->NU2; pts[3] = step->A; renderer_ops->draw_polyline(renderer,pts,sizeof(pts)/sizeof(pts[0]), &color_black); pts[0] = step->D; pts[1] = step->SD1; pts[2] = step->SD2; pts[3] = step->south.pos; renderer_ops->draw_polyline(renderer,pts,sizeof(pts)/sizeof(pts[0]), &color_black); if ((step->type == STEP_INITIAL) || (step->type == STEP_MACROCALL) || (step->type == STEP_SUBPCALL)) { renderer_ops->fill_rect(renderer, &step->I, &step->J, &color_white); renderer_ops->draw_rect(renderer, &step->I, &step->J, &color_black); } else { renderer_ops->fill_rect(renderer, &step->E, &step->F, &color_white); } renderer_ops->draw_rect(renderer, &step->E, &step->F, &color_black); if (step->type != STEP_MACROENTRY) renderer_ops->draw_line(renderer,&step->A,&step->B,&color_black); if (step->type != STEP_MACROEXIT) renderer_ops->draw_line(renderer,&step->C,&step->D,&color_black); renderer_ops->set_font(renderer, step->font, step->font_size); renderer_ops->draw_string(renderer, step->id, &step->G, ALIGN_CENTER, &step->font_color); if (step->active) renderer_ops->fill_ellipse(renderer, &step->H, STEP_DOT_RADIUS,STEP_DOT_RADIUS, &color_red); } static void step_update_data(Step *step) { Element *elem = &step->element; DiaObject *obj = &elem->object; ElementBBExtras *extra = &elem->extra_spacing; Point *p,ulc; ulc = elem->corner; ulc.x += ((STEP_DECLAREDWIDTH - STEP_WIDTH) / 2.0); /* we cheat a little */ step->A.x = 0.0 + (STEP_WIDTH / 2.0); step->A.y = 0.0; step->D.x = 0.0 + (STEP_WIDTH / 2.0); step->D.y = 0.0 + STEP_HEIGHT; step->E.x = 0.0; step->E.y = 0.5; step->F.x = STEP_WIDTH; step->F.y = STEP_HEIGHT- 0.5; switch(step->type) { case STEP_INITIAL: step->I.x = step->E.x - 2 * STEP_LINE_WIDTH; step->I.y = step->E.y - 2 * STEP_LINE_WIDTH; step->J.x = step->F.x + 2 * STEP_LINE_WIDTH; step->J.y = step->F.y + 2 * STEP_LINE_WIDTH; step->B.x = step->A.x; step->B.y = step->I.y; step->C.x = step->D.x; step->C.y = step->J.y; step->Z.x = step->J.x; step->Z.y = STEP_HEIGHT / 2; break; case STEP_MACROCALL: step->I.x = step->E.x; step->I.y = step->E.y - 2 * STEP_LINE_WIDTH; step->J.x = step->F.x; step->J.y = step->F.y + 2 * STEP_LINE_WIDTH; step->B.x = step->A.x; step->B.y = step->I.y; step->C.x = step->D.x; step->C.y = step->J.y; step->Z.x = step->J.x; step->Z.y = STEP_HEIGHT / 2; break; case STEP_SUBPCALL: step->I.x = step->E.x - 2 * STEP_LINE_WIDTH; step->I.y = step->E.y; step->J.x = step->F.x + 2 * STEP_LINE_WIDTH; step->J.y = step->F.y; step->B.x = step->A.x; step->B.y = step->I.y; step->C.x = step->D.x; step->C.y = step->J.y; step->Z.x = step->J.x; step->Z.y = STEP_HEIGHT / 2; break; default: /* regular or macro end steps */ step->B.x = step->A.x; step->B.y = step->E.y; step->C.x = step->D.x; step->C.y = step->F.y; step->Z.x = step->F.x; step->Z.y = STEP_HEIGHT / 2; } step->G.x = step->A.x; step->G.y = (STEP_HEIGHT / 2) + (.3 * step->font_size); step->H.x = step->E.x + (1.2 * STEP_DOT_RADIUS); step->H.y = step->F.y - (1.2 * STEP_DOT_RADIUS); for (p=&(step->A); p<=&(step->Z) ; p++) point_add(p,&ulc); /* Update handles: */ if (step->north.pos.x == -65536.0) { step->north.pos = step->A; step->south.pos = step->D; } step->NU1.x = step->north.pos.x; step->NU2.x = step->A.x; step->NU1.y = step->NU2.y = (step->north.pos.y + step->A.y) / 2.0; step->SD1.x = step->D.x; step->SD2.x = step->south.pos.x; step->SD1.y = step->SD2.y = (step->south.pos.y + step->D.y) / 2.0; /* Update connections: */ step->connections[0].pos = step->A; step->connections[0].directions = DIR_EAST|DIR_WEST; step->connections[1].pos = step->D; step->connections[1].directions = DIR_EAST|DIR_WEST; step->connections[2].pos = step->Z; step->connections[2].directions = DIR_EAST; step->connections[3].pos = step->H; step->connections[3].directions = DIR_ALL; /* recalc the bounding box : */ if ((step->type == STEP_INITIAL) || (step->type == STEP_SUBPCALL)) { extra->border_trans = 2.5 * STEP_LINE_WIDTH; } else { extra->border_trans = STEP_LINE_WIDTH / 2; } element_update_boundingbox(elem); rectangle_add_point(&obj->bounding_box,&step->north.pos); rectangle_add_point(&obj->bounding_box,&step->south.pos); obj->position = elem->corner; element_update_handles(elem); } static DiaObject * step_create(Point *startpoint, void *user_data, Handle **handle1, Handle **handle2) { Step *step; Element *elem; DiaObject *obj; int i; int type; step = g_new0(Step,1); elem = &step->element; obj = &elem->object; obj->type = &step_type; obj->ops = &step_ops; elem->corner = *startpoint; elem->width = STEP_DECLAREDWIDTH; elem->height = STEP_HEIGHT; element_init(elem, 10, 4); for (i=0;i<4;i++) { obj->connections[i] = &step->connections[i]; step->connections[i].object = obj; step->connections[i].connected = NULL; } step->id = new_step_name(); step->active = 0; step->font = dia_font_new_from_style (STEP_FONT,STEP_FONT_HEIGHT); step->font_size = STEP_FONT_HEIGHT; step->font_color = color_black; type = GPOINTER_TO_INT(user_data); switch(type) { case STEP_NORMAL: case STEP_INITIAL: case STEP_MACROENTRY: case STEP_MACROEXIT: case STEP_MACROCALL: case STEP_SUBPCALL: step->type = type; break; default: step->type = STEP_NORMAL; } for (i=0;i<8;i++) { obj->handles[i]->type = HANDLE_NON_MOVABLE; } obj->handles[8] = &step->north; obj->handles[9] = &step->south; step->north.connect_type = HANDLE_CONNECTABLE; step->north.type = HANDLE_MAJOR_CONTROL; step->north.id = HANDLE_NORTH; step->south.connect_type = HANDLE_CONNECTABLE; step->south.type = HANDLE_MAJOR_CONTROL; step->south.id = HANDLE_SOUTH; step->north.pos.x = -65536.0; /* magic */ step_update_data(step); *handle1 = NULL; *handle2 = obj->handles[0]; return &step->element.object; } static void step_destroy(Step *step) { dia_font_unref(step->font); g_free(step->id); element_destroy(&step->element); } static DiaObject * step_load(ObjectNode obj_node, int version, const char *filename) { return object_load_using_properties(&step_type, obj_node,version,filename); }