Hi,
appended to this mail is a patch for state clusters. They are like
states but you can resize them. They are intended as borders which you
can use to group states.
You can think of a transition starting at a state cluster as equivalent
to lots of transitions starting at the contained states.
Transitions ending at state clusters don't make much sense if you want a
deterministic state machine.
The source is strongly influenced by state.c and large_package.c (mostly
copy&paste...)
To get an icon in the toolbox, I inserted some lines in UML.sheet.
<object name="UML - State Cluster">
<description>State Cluster</description>
<description xml:lang="de">Zustandsgruppe</description>
<description xml:lang="en_CA">State Cluster</description>
<description xml:lang="en_GB">State Cluster</description>
</object>
But this file is generated from somewhere else. Where would have been
the right place to insert the new shape?
Sebastian
Index: objects/UML/pixmaps/state_cluster.xpm
===================================================================
--- objects/UML/pixmaps/state_cluster.xpm (Revision 0)
+++ objects/UML/pixmaps/state_cluster.xpm (Revision 0)
@@ -0,0 +1,31 @@
+/* XPM */
+static char * state_cluster_xpm[] = {
+/* width height num_colors chars_per_pixel */
+"22 22 3 1",
+/* colors */
+". c none",
+"+ c black",
+" c white",
+/* pixels */
+"......................",
+"...++++++++++++++++...",
+"..+ +..",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+".+ +.",
+"..+ +..",
+"...++++++++++++++++...",
+"......................",
+"......................",
+"......................"};
Index: objects/UML/state_cluster.c
===================================================================
--- objects/UML/state_cluster.c (Revision 0)
+++ objects/UML/state_cluster.c (Revision 0)
@@ -0,0 +1,480 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * 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 <config.h>
+#endif
+
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+
+#include "intl.h"
+#include "object.h"
+#include "element.h"
+#include "diarenderer.h"
+#include "attributes.h"
+#include "text.h"
+#include "properties.h"
+
+#include "pixmaps/state_cluster.xpm"
+
+typedef struct _State State;
+
+typedef enum {
+ ENTRY_ACTION,
+ DO_ACTION,
+ EXIT_ACTION
+} StateAction;
+
+#define NUM_CONNECTIONS 9
+
+struct _State {
+ Element element;
+
+ ConnectionPoint connections[NUM_CONNECTIONS];
+
+ Text *text;
+
+ TextAttributes attrs;
+
+ Color line_color;
+ Color fill_color;
+
+ gchar* entry_action;
+ gchar* do_action;
+ gchar* exit_action;
+};
+
+
+#define STATE_WIDTH 4
+#define STATE_HEIGHT 3
+#define STATE_LINEWIDTH 0.1
+#define STATE_MARGIN_X 0.5
+#define STATE_MARGIN_Y 0.5
+
+static real state_distance_from(State *state, Point *point);
+static void state_select(State *state, Point *clicked_point,
+ DiaRenderer *interactive_renderer);
+static ObjectChange* state_move_handle(State *state, Handle *handle,
+ Point *to, ConnectionPoint *cp,
+ HandleMoveReason reason, ModifierKeys modifiers);
+static ObjectChange* state_move(State *state, Point *to);
+static void state_draw(State *state, DiaRenderer *renderer);
+static DiaObject *state_create(Point *startpoint,
+ void *user_data,
+ Handle **handle1,
+ Handle **handle2);
+static void state_destroy(State *state);
+static DiaObject *state_load(ObjectNode obj_node, int version,
+ const char *filename);
+static PropDescription *state_describe_props(State *state);
+static void state_get_props(State *state, GPtrArray *props);
+static void state_set_props(State *state, GPtrArray *props);
+static void state_update_data(State *state);
+static gchar* state_get_action_text(State* state, StateAction action);
+static void state_calc_action_text_pos(State* state, StateAction action, Point* pos);
+
+static ObjectTypeOps state_type_ops =
+{
+ (CreateFunc) state_create,
+ (LoadFunc) state_load,/*using_properties*/ /* load */
+ (SaveFunc) object_save_using_properties, /* save */
+ (GetDefaultsFunc) NULL,
+ (ApplyDefaultsFunc) NULL
+};
+
+DiaObjectType state_cluster_type =
+{
+ "UML - State Cluster", /* name */
+ 0, /* version */
+ (char **) state_cluster_xpm, /* pixmap */
+
+ &state_type_ops /* ops */
+};
+
+static ObjectOps state_ops = {
+ (DestroyFunc) state_destroy,
+ (DrawFunc) state_draw,
+ (DistanceFunc) state_distance_from,
+ (SelectFunc) state_select,
+ (CopyFunc) object_copy_using_properties,
+ (MoveFunc) state_move,
+ (MoveHandleFunc) state_move_handle,
+ (GetPropertiesFunc) object_create_props_dialog,
+ (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
+ (ObjectMenuFunc) NULL,
+ (DescribePropsFunc) state_describe_props,
+ (GetPropsFunc) state_get_props,
+ (SetPropsFunc) state_set_props,
+ (TextEditFunc) 0,
+ (ApplyPropertiesListFunc) object_apply_props,
+};
+
+static PropDescription state_props[] = {
+ ELEMENT_COMMON_PROPERTIES,
+ PROP_STD_LINE_COLOUR_OPTIONAL,
+ PROP_STD_FILL_COLOUR_OPTIONAL,
+ PROP_STD_TEXT_FONT,
+ PROP_STD_TEXT_HEIGHT,
+ PROP_STD_TEXT_COLOUR_OPTIONAL,
+ { "text", PROP_TYPE_TEXT, 0, N_("Text"), NULL, NULL },
+ { "entry_action", PROP_TYPE_STRING, PROP_FLAG_OPTIONAL | PROP_FLAG_VISIBLE, N_("Entry action"), NULL, NULL },
+ { "do_action", PROP_TYPE_STRING, PROP_FLAG_OPTIONAL | PROP_FLAG_VISIBLE, N_("Do action"), NULL, NULL },
+ { "exit_action", PROP_TYPE_STRING, PROP_FLAG_OPTIONAL | PROP_FLAG_VISIBLE, N_("Exit action"), NULL, NULL },
+ PROP_DESC_END
+};
+
+static PropDescription *
+state_describe_props(State *state)
+{
+ if (state_props[0].quark == 0) {
+ prop_desc_list_calculate_quarks(state_props);
+ }
+ return state_props;
+}
+
+static PropOffset state_offsets[] = {
+ ELEMENT_COMMON_PROPERTIES_OFFSETS,
+ {"line_colour",PROP_TYPE_COLOUR,offsetof(State,line_color)},
+ {"fill_colour",PROP_TYPE_COLOUR,offsetof(State,fill_color)},
+ {"text",PROP_TYPE_TEXT,offsetof(State,text)},
+ {"text_font",PROP_TYPE_FONT,offsetof(State,attrs.font)},
+ {PROP_STDNAME_TEXT_HEIGHT,PROP_STDTYPE_TEXT_HEIGHT,offsetof(State,attrs.height)},
+ {"text_colour",PROP_TYPE_COLOUR,offsetof(State,attrs.color)},
+ {"entry_action",PROP_TYPE_STRING,offsetof(State,entry_action)},
+ {"do_action",PROP_TYPE_STRING,offsetof(State,do_action)},
+ {"exit_action",PROP_TYPE_STRING,offsetof(State,exit_action)},
+ { NULL, 0, 0 },
+};
+
+static void
+state_get_props(State * state, GPtrArray *props)
+{
+ text_get_attributes(state->text,&state->attrs);
+ object_get_props_from_offsets(
+ &state->element.object,
+ state_offsets,props);
+}
+
+static void
+state_set_props(State *state, GPtrArray *props)
+{
+ object_set_props_from_offsets(
+ &state->element.object,
+ state_offsets,props);
+ apply_textattr_properties(props,state->text,"text",&state->attrs);
+ state_update_data(state);
+}
+
+static real
+state_distance_from(State *state, Point *point)
+{
+ DiaObject *obj = &state->element.object;
+ return distance_rectangle_point(&obj->bounding_box, point);
+}
+
+static void
+state_select(
+ State *state, Point *clicked_point,
+ DiaRenderer *interactive_renderer)
+{
+ text_set_cursor(state->text, clicked_point, interactive_renderer);
+ text_grab_focus(state->text, &state->element.object);
+ element_update_handles(&state->element);
+}
+
+static ObjectChange*
+state_move_handle(
+ State *state, Handle *handle,
+ Point *to, ConnectionPoint *cp,
+ HandleMoveReason reason, ModifierKeys modifiers)
+{
+ assert(state!=NULL);
+ assert(handle!=NULL);
+ assert(to!=NULL);
+
+ assert(handle->id < 8);
+
+ element_move_handle(&state->element, handle->id, to, cp, reason, modifiers);
+ state_update_data(state);
+
+ return NULL;
+}
+
+static ObjectChange*
+state_move(State *state, Point *to)
+{
+ state->element.corner = *to;
+ state_update_data(state);
+
+ return NULL;
+}
+
+static void
+state_draw_action_string(State *state, DiaRenderer *renderer, StateAction action)
+{
+ DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
+ Point pos;
+ gchar* action_text = state_get_action_text(state, action);
+ state_calc_action_text_pos(state, action, &pos);
+ renderer_ops->set_font(renderer, state->text->font, state->text->height);
+ renderer_ops->draw_string(renderer,
+ action_text,
+ &pos,
+ ALIGN_LEFT,
+ &state->attrs.color);
+ g_free(action_text);
+}
+
+static void
+state_draw(State *state, DiaRenderer *renderer)
+{
+ DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
+ Element *elem;
+ real x, y, w, h;
+ Point p1, p2, split_line_left, split_line_right;
+ gboolean has_actions;
+
+ assert(state != NULL);
+ assert(renderer != NULL);
+
+ elem = &state->element;
+
+ x = elem->corner.x;
+ y = elem->corner.y;
+ w = elem->width;
+ h = elem->height;
+
+ renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
+ renderer_ops->set_linewidth(renderer, STATE_LINEWIDTH);
+ renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
+
+ p1.x = x;
+ p1.y = y;
+ p2.x = x + w;
+ p2.y = y + h;
+ renderer_ops->draw_rounded_rect(renderer, &p1, &p2, &state->line_color, 0.5);
+
+ text_draw(state->text, renderer);
+ has_actions = FALSE;
+ if (state->entry_action && strlen(state->entry_action) != 0) {
+ state_draw_action_string(state, renderer, ENTRY_ACTION);
+ has_actions = TRUE;
+ }
+ if (state->do_action && strlen(state->do_action) != 0) {
+ state_draw_action_string(state, renderer, DO_ACTION);
+ has_actions = TRUE;
+ }
+ if (state->exit_action && strlen(state->exit_action) != 0) {
+ state_draw_action_string(state, renderer, EXIT_ACTION);
+ has_actions = TRUE;
+ }
+
+ if (has_actions) {
+ split_line_left.x = x;
+ split_line_right.x = x+STATE_MARGIN_X+state->text->max_width;
+ split_line_left.y
+ = split_line_right.y
+ = state->element.corner.y + STATE_MARGIN_Y +
+ state->text->numlines*state->text->height;
+ renderer_ops->draw_line(renderer, &split_line_left, &split_line_right,
+ &state->line_color);
+ }
+}
+
+
+static void
+state_update_width_and_height_with_action_text(
+ State* state,
+ StateAction action,
+ real* width,
+ real* height)
+{
+ gchar* action_text = state_get_action_text(state, action);
+ *width = MAX(*width,
+ dia_font_string_width(action_text, state->text->font,
+ state->text->height)
+ + 2*STATE_MARGIN_X);
+ g_free(action_text);
+ *height += state->text->height;
+}
+
+static void
+state_update_data(State *state)
+{
+ real w, h;
+
+ Element *elem = &state->element;
+ ElementBBExtras *extra = &elem->extra_spacing;
+ DiaObject *obj = &elem->object;
+ Point p;
+
+ text_calc_boundingbox(state->text, NULL);
+ /* First consider the state description text */
+ w = state->text->max_width + 2*STATE_MARGIN_X;
+ h = state->text->height*state->text->numlines +2*STATE_MARGIN_Y;
+ if (w < STATE_WIDTH)
+ w = STATE_WIDTH;
+ /* Then consider the actions texts */
+ if (state->entry_action && strlen(state->entry_action) != 0) {
+ state_update_width_and_height_with_action_text(state, ENTRY_ACTION, &w, &h);
+ }
+ if (state->do_action && strlen(state->do_action) != 0) {
+ state_update_width_and_height_with_action_text(state, DO_ACTION, &w, &h);
+ }
+ if (state->exit_action && strlen(state->exit_action) != 0) {
+ state_update_width_and_height_with_action_text(state, EXIT_ACTION, &w, &h);
+ }
+
+ elem->width = MAX(elem->width, w);
+ elem->height = MAX(elem->height, h);
+ extra->border_trans = STATE_LINEWIDTH / 2.0;
+
+ p.x = elem->corner.x + STATE_MARGIN_X;
+ p.y = elem->corner.y + STATE_MARGIN_Y + state->text->ascent;
+ text_set_position(state->text, &p);
+ text_set_alignment(state->text, ALIGN_LEFT);
+
+ /* Update connections: */
+ element_update_connections_rectangle(elem, state->connections);
+
+ element_update_boundingbox(elem);
+
+ obj->position = elem->corner;
+
+ element_update_handles(elem);
+}
+
+static DiaObject *
+state_create(Point *startpoint,
+ void *user_data,
+ Handle **handle1,
+ Handle **handle2)
+{
+ State *state;
+ Element *elem;
+ DiaObject *obj;
+ Point p;
+ DiaFont *font;
+ int i;
+
+ state = g_malloc0(sizeof(State));
+ elem = &state->element;
+ obj = &elem->object;
+
+ obj->type = &state_cluster_type;
+ obj->ops = &state_ops;
+ elem->corner = *startpoint;
+ elem->width = STATE_WIDTH;
+ elem->height = STATE_HEIGHT;
+
+ state->line_color = attributes_get_foreground();
+ state->fill_color = attributes_get_background();
+
+ font = dia_font_new_from_style(DIA_FONT_SANS, 0.8);
+ p = *startpoint;
+ p.x += STATE_WIDTH/2.0;
+ p.y += STATE_HEIGHT/2.0;
+
+ state->text = new_text("", font, 0.8, &p, &color_black, ALIGN_LEFT);
+ text_get_attributes(state->text,&state->attrs);
+
+ dia_font_unref(font);
+
+ element_init(elem, 8, NUM_CONNECTIONS);
+
+ for (i=0;i<NUM_CONNECTIONS;i++) {
+ obj->connections[i] = &state->connections[i];
+ state->connections[i].object = obj;
+ state->connections[i].connected = NULL;
+ }
+ state->connections[8].flags = CP_FLAGS_MAIN;
+ elem->extra_spacing.border_trans = 0.0;
+ state_update_data(state);
+
+ *handle1 = NULL;
+ *handle2 = NULL;
+ return &state->element.object;
+}
+
+static void
+state_destroy(State *state)
+{
+ text_destroy(state->text);
+
+ element_destroy(&state->element);
+}
+
+static DiaObject *
+state_load(ObjectNode obj_node, int version, const char *filename)
+{
+ return object_load_using_properties(&state_cluster_type,
+ obj_node,version,filename);
+}
+
+static void
+state_calc_action_text_pos(State* state, StateAction action, Point* pos)
+{
+ int entry_action_valid = state->entry_action && strlen(state->entry_action) != 0;
+ int do_action_valid = state->do_action && strlen(state->do_action) != 0;
+
+ real first_action_y = state->text->numlines*state->text->height +
+ state->text->position.y;
+
+ pos->x = state->element.corner.x + STATE_MARGIN_X;
+
+ switch (action)
+ {
+ case ENTRY_ACTION:
+ pos->y = first_action_y;
+ break;
+
+ case DO_ACTION:
+ pos->y = first_action_y;
+ if (entry_action_valid) pos->y += state->text->height;
+ break;
+
+ case EXIT_ACTION:
+ pos->y = first_action_y;
+ if (entry_action_valid) pos->y += state->text->height;
+ if (do_action_valid) pos->y += state->text->height;
+ break;
+ }
+}
+
+
+static gchar*
+state_get_action_text(State* state, StateAction action)
+{
+ switch (action)
+ {
+ case ENTRY_ACTION:
+ return g_strdup_printf("entry/ %s", state->entry_action);
+ break;
+
+ case DO_ACTION:
+ return g_strdup_printf("do/ %s", state->do_action);
+ break;
+
+ case EXIT_ACTION:
+ return g_strdup_printf("exit/ %s", state->exit_action);
+ break;
+ }
+ return NULL;
+}
Index: objects/UML/Makefile.am
===================================================================
--- objects/UML/Makefile.am (Revision 4358)
+++ objects/UML/Makefile.am (Arbeitskopie)
@@ -25,6 +25,7 @@
component_feature.c \
classicon.c \
state.c \
+ state_cluster.c \
activity.c \
state_term.c \
node.c \
@@ -63,6 +64,7 @@
pixmaps/realizes.xpm \
pixmaps/smallpackage.xpm \
pixmaps/state.xpm \
+ pixmaps/state_cluster.xpm \
pixmaps/umlclass.xpm \
pixmaps/node.xpm \
pixmaps/branch.xpm \
Index: objects/UML/uml.c
===================================================================
--- objects/UML/uml.c (Revision 4358)
+++ objects/UML/uml.c (Arbeitskopie)
@@ -49,6 +49,7 @@
extern DiaObjectType component_type;
extern DiaObjectType classicon_type;
extern DiaObjectType state_type;
+extern DiaObjectType state_cluster_type;
extern DiaObjectType activity_type;
extern DiaObjectType node_type;
extern DiaObjectType branch_type;
@@ -86,6 +87,7 @@
object_register_type(&component_type);
object_register_type(&classicon_type);
object_register_type(&state_type);
+ object_register_type(&state_cluster_type);
object_register_type(&state_term_type);
object_register_type(&activity_type);
object_register_type(&node_type);
_______________________________________________
dia-list mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/dia-list
FAQ at http://live.gnome.org/Dia/Faq
Main page at http://live.gnome.org/Dia