/* Dia -- an diagram creation/manipulation program * Copyright (C) 1998 Alexander Larsson * * Jackson diagram - adapted by Christophe Ponsard * This class captures all kind of domains (given, designed, machine) * both for generic problems and for problem frames (ie. with domain kinds) * * based on SADT diagrams copyright (C) 2000, 2001 Cyrille Chepelov * * Forked from Flowchart toolbox -- objects for drawing flowcharts. * Copyright (C) 1999 James Henstridge. * * 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 "connection.h" #include "diarenderer.h" #include "handle.h" #include "arrows.h" #include "properties.h" #include "pixmaps/shared_phen.xpm" typedef struct _Message Message; typedef enum { MSG_SHARED, MSG_REQ, } MessageType; struct _Message { Connection connection; Handle text_handle; gchar *text; Point text_pos; real text_width; MessageType type; int init; }; #define MESSAGE_WIDTH 0.09 #define MESSAGE_DASHLEN 0.5 #define MESSAGE_FONTHEIGHT 0.7 #define MESSAGE_ARROWLEN 0.8 #define MESSAGE_ARROWWIDTH 0.5 #define HANDLE_MOVE_TEXT (HANDLE_CUSTOM1) static DiaFont *message_font = NULL; static ObjectChange* message_move_handle(Message *message, Handle *handle, Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers); static ObjectChange* message_move(Message *message, Point *to); static void message_select(Message *message, Point *clicked_point, DiaRenderer *interactive_renderer); static void message_draw(Message *message, DiaRenderer *renderer); static DiaObject *message_create(Point *startpoint, void *user_data, Handle **handle1, Handle **handle2); static real message_distance_from(Message *message, Point *point); static void message_update_data(Message *message); static void message_destroy(Message *message); static DiaObject *message_load(ObjectNode obj_node, int version, const char *filename); static PropDescription *message_describe_props(Message *mes); static void message_get_props(Message * message, GPtrArray *props); static void message_set_props(Message *message, GPtrArray *props); static ObjectTypeOps message_type_ops = { (CreateFunc) message_create, (LoadFunc) message_load,/*using_properties*/ /* load */ (SaveFunc) object_save_using_properties, /* save */ (GetDefaultsFunc) NULL, (ApplyDefaultsFunc) NULL }; DiaObjectType jackson_phenomenon_type = { "Jackson - phenomenon", /* name */ 0, /* version */ (char **)jackson_shared_phen_xpm, /* pixmap */ &message_type_ops /* ops */ }; static ObjectOps message_ops = { (DestroyFunc) message_destroy, (DrawFunc) message_draw, (DistanceFunc) message_distance_from, (SelectFunc) message_select, (CopyFunc) object_copy_using_properties, (MoveFunc) message_move, (MoveHandleFunc) message_move_handle, (GetPropertiesFunc) object_create_props_dialog, (ApplyPropertiesFunc) object_apply_props_from_dialog, (ObjectMenuFunc) NULL, (DescribePropsFunc) message_describe_props, (GetPropsFunc) message_get_props, (SetPropsFunc) message_set_props }; static PropEnumData prop_message_type_data[] = { { N_("Shared"), MSG_SHARED }, { N_("Requirement"), MSG_REQ }, { NULL, 0} }; static PropDescription message_props[] = { CONNECTION_COMMON_PROPERTIES, { "text", PROP_TYPE_STRING, PROP_FLAG_VISIBLE, N_("Message:"), NULL, NULL }, { "type", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE, N_("Type:"), NULL, prop_message_type_data }, { "text_pos", PROP_TYPE_POINT, 0, "text_pos:", NULL,NULL }, PROP_DESC_END }; static PropDescription * message_describe_props(Message *mes) { if (message_props[0].quark == 0) prop_desc_list_calculate_quarks(message_props); return message_props; } static PropOffset message_offsets[] = { CONNECTION_COMMON_PROPERTIES_OFFSETS, { "text", PROP_TYPE_STRING, offsetof(Message, text) }, { "type", PROP_TYPE_ENUM, offsetof(Message, type) }, { "text_pos", PROP_TYPE_POINT, offsetof(Message,text_pos) }, { NULL, 0, 0 } }; static void message_get_props(Message * message, GPtrArray *props) { object_get_props_from_offsets(&message->connection.object, message_offsets, props); } static void message_set_props(Message *message, GPtrArray *props) { if (message->init==-1) { message->init++; return; } object_set_props_from_offsets(&message->connection.object, message_offsets, props); message_update_data(message); } static real message_distance_from(Message *message, Point *point) { Point *endpoints; real dist; endpoints = &message->connection.endpoints[0]; dist = distance_line_point(&endpoints[0], &endpoints[1], MESSAGE_WIDTH, point); return dist; } static void message_select(Message *message, Point *clicked_point, DiaRenderer *interactive_renderer) { connection_update_handles(&message->connection); } static ObjectChange* message_move_handle(Message *message, Handle *handle, Point *to, ConnectionPoint *cp, HandleMoveReason reason, ModifierKeys modifiers) { Point p1, p2; Point *endpoints; assert(message!=NULL); assert(handle!=NULL); assert(to!=NULL); if (handle->id == HANDLE_MOVE_TEXT) { message->text_pos = *to; } else { endpoints = &message->connection.endpoints[0]; p1.x = 0.5*(endpoints[0].x + endpoints[1].x); p1.y = 0.5*(endpoints[0].y + endpoints[1].y); connection_move_handle(&message->connection, handle->id, to, cp, reason, modifiers); connection_adjust_for_autogap(&message->connection); p2.x = 0.5*(endpoints[0].x + endpoints[1].x); p2.y = 0.5*(endpoints[0].y + endpoints[1].y); point_sub(&p2, &p1); point_add(&message->text_pos, &p2); } message_update_data(message); return NULL; } static ObjectChange* message_move(Message *message, Point *to) { Point start_to_end; Point *endpoints = &message->connection.endpoints[0]; Point delta; delta = *to; point_sub(&delta, &endpoints[0]); start_to_end = endpoints[1]; point_sub(&start_to_end, &endpoints[0]); endpoints[1] = endpoints[0] = *to; point_add(&endpoints[1], &start_to_end); point_add(&message->text_pos, &delta); message_update_data(message); return NULL; } /* drawing here -- TBD inverse flow ?? */ static void message_draw(Message *message, DiaRenderer *renderer) { DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer); Point *endpoints, p1, p2; Arrow arrow; int n1 = 1, n2 = 0; gchar *mname = g_strdup(message->text); /* some asserts */ assert(message != NULL); assert(renderer != NULL); /* arrow type */ endpoints = &message->connection.endpoints[0]; renderer_ops->set_linewidth(renderer, MESSAGE_WIDTH); renderer_ops->set_linecaps(renderer, LINECAPS_BUTT); if (message->type==MSG_REQ) { renderer_ops->set_dashlength(renderer, MESSAGE_DASHLEN); renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED); arrow.type = ARROW_FILLED_TRIANGLE; } else { renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID); arrow.type = ARROW_NONE; } arrow.length = MESSAGE_ARROWLEN; arrow.width = MESSAGE_ARROWWIDTH; /* could reverse direction here */ p1 = endpoints[n1]; p2 = endpoints[n2]; /* drawing directed line */ renderer_ops->draw_line_with_arrows(renderer, &p1, &p2, MESSAGE_WIDTH, &color_black, &arrow, NULL); /* writing text on arrow (maybe not a good idea) */ renderer_ops->set_font(renderer, message_font, MESSAGE_FONTHEIGHT); if (mname && strlen(mname) != 0) renderer_ops->draw_string(renderer, mname, &message->text_pos, ALIGN_CENTER, &color_black); if (mname) g_free(mname); } /* creation here */ static DiaObject * message_create(Point *startpoint, void *user_data, Handle **handle1, Handle **handle2) { Message *message; Connection *conn; LineBBExtras *extra; DiaObject *obj; if (message_font == NULL) { message_font = dia_font_new_from_style (DIA_FONT_SANS, MESSAGE_FONTHEIGHT); } message = g_malloc0(sizeof(Message)); conn = &message->connection; conn->endpoints[0] = *startpoint; conn->endpoints[1] = *startpoint; conn->endpoints[1].x += 1.5; obj = &conn->object; extra = &conn->extra_spacing; obj->type = &jackson_phenomenon_type; obj->ops = &message_ops; connection_init(conn, 3, 0); message->text = g_strdup(""); message->text_width = 0.0; message->text_pos.x = 0.5*(conn->endpoints[0].x + conn->endpoints[1].x); message->text_pos.y = 0.5*(conn->endpoints[0].y + conn->endpoints[1].y); message->text_handle.id = HANDLE_MOVE_TEXT; message->text_handle.type = HANDLE_MINOR_CONTROL; message->text_handle.connect_type = HANDLE_NONCONNECTABLE; message->text_handle.connected_to = NULL; obj->handles[2] = &message->text_handle; extra->start_long = extra->start_trans = extra->end_long = MESSAGE_WIDTH/2.0; extra->end_trans = MAX(MESSAGE_WIDTH,MESSAGE_ARROWLEN)/2.0; message_update_data(message); *handle1 = obj->handles[0]; *handle2 = obj->handles[1]; /* init type */ switch (GPOINTER_TO_INT(user_data)) { case 1: message->type=MSG_SHARED; break; case 2: message->type=MSG_REQ; break; default: message->type=MSG_SHARED; break; } if (GPOINTER_TO_INT(user_data)!=0) message->init=-1; else message->init=0; return &message->connection.object; } static void message_destroy(Message *message) { connection_destroy(&message->connection); g_free(message->text); } static void message_update_data(Message *message) { Connection *conn = &message->connection; DiaObject *obj = &conn->object; Rectangle rect; if (connpoint_is_autogap(conn->endpoint_handles[0].connected_to) || connpoint_is_autogap(conn->endpoint_handles[1].connected_to)) { connection_adjust_for_autogap(conn); } obj->position = conn->endpoints[0]; message->text_handle.pos = message->text_pos; connection_update_handles(conn); connection_update_boundingbox(conn); message->text_width = dia_font_string_width(message->text, message_font, MESSAGE_FONTHEIGHT); /* Add boundingbox for text: */ rect.left = message->text_pos.x-message->text_width/2; rect.right = rect.left + message->text_width; rect.top = message->text_pos.y - dia_font_ascent(message->text, message_font, MESSAGE_FONTHEIGHT); rect.bottom = rect.top + MESSAGE_FONTHEIGHT; rectangle_union(&obj->bounding_box, &rect); } static DiaObject * message_load(ObjectNode obj_node, int version, const char *filename) { return object_load_using_properties(&jackson_phenomenon_type, obj_node,version,filename); }