1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 *
4 * GRAFCET chart support
5 * Copyright(C) 2000 Cyrille Chepelov
6 *
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.
11 *
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.
16 *
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.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <assert.h>
27 #include <math.h>
28
29 #include "intl.h"
30 #include "object.h"
31 #include "connection.h"
32 #include "connectionpoint.h"
33 #include "diarenderer.h"
34 #include "attributes.h"
35 #include "widgets.h"
36 #include "message.h"
37 #include "color.h"
38 #include "properties.h"
39 #include "geometry.h"
40 #include "connpoint_line.h"
41
42 #include "grafcet.h"
43
44 #include "pixmaps/vergent.xpm"
45
46 #define VERGENT_LINE_WIDTH (GRAFCET_GENERAL_LINE_WIDTH * 1.5)
47
48 typedef enum { VERGENT_OR, VERGENT_AND } VergentType;
49
50 typedef struct _Vergent {
51 Connection connection;
52
53 ConnectionPoint northeast,northwest,southwest,southeast;
54 ConnPointLine *north,*south;
55
56 VergentType type;
57 } Vergent;
58
59 static ObjectChange* vergent_move_handle(Vergent *vergent, Handle *handle,
60 Point *to, ConnectionPoint *cp,
61 HandleMoveReason reason,
62 ModifierKeys modifiers);
63 static ObjectChange* vergent_move(Vergent *vergent, Point *to);
64 static void vergent_select(Vergent *vergent, Point *clicked_point,
65 DiaRenderer *interactive_renderer);
66 static void vergent_draw(Vergent *vergent, DiaRenderer *renderer);
67 static DiaObject *vergent_create(Point *startpoint,
68 void *user_data,
69 Handle **handle1,
70 Handle **handle2);
71 static real vergent_distance_from(Vergent *vergent, Point *point);
72 static void vergent_update_data(Vergent *vergent);
73 static void vergent_destroy(Vergent *vergent);
74 static DiaObject *vergent_load(ObjectNode obj_node, int version,
75 const char *filename);
76 static PropDescription *vergent_describe_props(Vergent *vergent);
77 static void vergent_get_props(Vergent *vergent,
78 GPtrArray *props);
79 static void vergent_set_props(Vergent *vergent,
80 GPtrArray *props);
81
82 static DiaMenu *vergent_get_object_menu(Vergent *vergent,
83 Point *clickedpoint);
84
85 static ObjectTypeOps vergent_type_ops =
86 {
87 (CreateFunc)vergent_create, /* create */
88 (LoadFunc) vergent_load,/*using properties*/ /* load */
89 (SaveFunc) object_save_using_properties, /* save */
90 (GetDefaultsFunc) NULL,
91 (ApplyDefaultsFunc) NULL
92 };
93
94 DiaObjectType vergent_type =
95 {
96 "GRAFCET - Vergent", /* name */
97 0, /* version */
98 (char **) vergent_xpm, /* pixmap */
99
100 &vergent_type_ops /* ops */
101 };
102
103
104 static ObjectOps vergent_ops = {
105 (DestroyFunc) vergent_destroy,
106 (DrawFunc) vergent_draw,
107 (DistanceFunc) vergent_distance_from,
108 (SelectFunc) vergent_select,
109 (CopyFunc) object_copy_using_properties,
110 (MoveFunc) vergent_move,
111 (MoveHandleFunc) vergent_move_handle,
112 (GetPropertiesFunc) object_create_props_dialog,
113 (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
114 (ObjectMenuFunc) vergent_get_object_menu,
115 (DescribePropsFunc) vergent_describe_props,
116 (GetPropsFunc) vergent_get_props,
117 (SetPropsFunc) vergent_set_props,
118 (TextEditFunc) 0,
119 (ApplyPropertiesListFunc) object_apply_props,
120
121 };
122
123 static PropEnumData prop_vtype_data[] = {
124 { N_("OR"), VERGENT_OR },
125 { N_("AND"), VERGENT_AND },
126 { NULL, 0 }
127 };
128
129 static PropDescription vergent_props[] = {
130 CONNECTION_COMMON_PROPERTIES,
131 { "cpl_north",PROP_TYPE_CONNPOINT_LINE, 0,
132 "cpl_north","cpl_north"},
133 { "cpl_south",PROP_TYPE_CONNPOINT_LINE, 0,
134 "cpl_south","cpl_south"},
135 { "vtype", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE|PROP_FLAG_NO_DEFAULTS,
136 N_("Vergent type:"),NULL, prop_vtype_data},
137 PROP_DESC_END
138 };
139
140 static PropDescription *
vergent_describe_props(Vergent * vergent)141 vergent_describe_props(Vergent *vergent)
142 {
143 if (vergent_props[0].quark == 0) {
144 prop_desc_list_calculate_quarks(vergent_props);
145 }
146 return vergent_props;
147 }
148
149 static PropOffset vergent_offsets[] = {
150 CONNECTION_COMMON_PROPERTIES_OFFSETS,
151 {"cpl_north",PROP_TYPE_CONNPOINT_LINE,offsetof(Vergent,north)},
152 {"cpl_south",PROP_TYPE_CONNPOINT_LINE,offsetof(Vergent,south)},
153 {"vtype",PROP_TYPE_ENUM,offsetof(Vergent,type)},
154 { NULL,0,0 }
155 };
156
157 static void
vergent_get_props(Vergent * vergent,GPtrArray * props)158 vergent_get_props(Vergent *vergent, GPtrArray *props)
159 {
160 object_get_props_from_offsets(&vergent->connection.object,
161 vergent_offsets,props);
162 }
163
164 static void
vergent_set_props(Vergent * vergent,GPtrArray * props)165 vergent_set_props(Vergent *vergent, GPtrArray *props)
166 {
167 object_set_props_from_offsets(&vergent->connection.object,
168 vergent_offsets,props);
169 vergent_update_data(vergent);
170 }
171
172
173 static real
vergent_distance_from(Vergent * vergent,Point * point)174 vergent_distance_from(Vergent *vergent, Point *point)
175 {
176 Connection *conn = &vergent->connection;
177 Rectangle rectangle;
178
179 rectangle.left = conn->endpoints[0].x;
180 rectangle.right = conn->endpoints[1].x;
181 rectangle.top = conn->endpoints[0].y;
182 switch (vergent->type) {
183 case VERGENT_OR:
184 rectangle.top -= .5 * VERGENT_LINE_WIDTH;
185 rectangle.bottom = rectangle.top + VERGENT_LINE_WIDTH;
186 break;
187 case VERGENT_AND:
188 rectangle.top -= 1.5 * VERGENT_LINE_WIDTH;
189 rectangle.bottom = rectangle.top + (3.0 * VERGENT_LINE_WIDTH);
190 break;
191 }
192 return distance_rectangle_point(&rectangle,point);
193 }
194
195 static void
vergent_select(Vergent * vergent,Point * clicked_point,DiaRenderer * interactive_renderer)196 vergent_select(Vergent *vergent, Point *clicked_point,
197 DiaRenderer *interactive_renderer)
198 {
199 vergent_update_data(vergent);
200 }
201
202 static ObjectChange*
vergent_move_handle(Vergent * vergent,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)203 vergent_move_handle(Vergent *vergent, Handle *handle,
204 Point *to, ConnectionPoint *cp,
205 HandleMoveReason reason, ModifierKeys modifiers)
206 {
207 g_assert(vergent!=NULL);
208 g_assert(handle!=NULL);
209 g_assert(to!=NULL);
210
211 if (handle->id == HANDLE_MOVE_ENDPOINT) {
212 Point to2;
213
214 to2.x = to->x;
215 to2.y = vergent->connection.endpoints[0].y;
216 connection_move_handle(&vergent->connection, HANDLE_MOVE_ENDPOINT,
217 &to2, NULL, reason, 0);
218 }
219 connection_move_handle(&vergent->connection, handle->id, to, cp,
220 reason, modifiers);
221 vergent_update_data(vergent);
222
223 return NULL;
224 }
225
226
227 static ObjectChange*
vergent_move(Vergent * vergent,Point * to)228 vergent_move(Vergent *vergent, Point *to)
229 {
230 Point start_to_end;
231 Point *endpoints = &vergent->connection.endpoints[0];
232
233 start_to_end = endpoints[1];
234 point_sub(&start_to_end, &endpoints[0]);
235
236 endpoints[1] = endpoints[0] = *to;
237 point_add(&endpoints[1], &start_to_end);
238
239 vergent_update_data(vergent);
240
241 return NULL;
242 }
243
244
245 static void
vergent_draw(Vergent * vergent,DiaRenderer * renderer)246 vergent_draw(Vergent *vergent, DiaRenderer *renderer)
247 {
248 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
249 Connection *conn = &vergent->connection;
250 Point p1,p2;
251
252 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
253
254 switch(vergent->type) {
255 case VERGENT_OR:
256 renderer_ops->set_linewidth(renderer, VERGENT_LINE_WIDTH);
257 renderer_ops->draw_line(renderer,
258 &conn->endpoints[0],&conn->endpoints[1],
259 &color_black);
260 break;
261 case VERGENT_AND:
262 renderer_ops->set_linewidth(renderer, 2.0 * VERGENT_LINE_WIDTH);
263 renderer_ops->draw_line(renderer,
264 &conn->endpoints[0],&conn->endpoints[1],
265 &color_white);
266 renderer_ops->set_linewidth(renderer, VERGENT_LINE_WIDTH);
267 p1.x = conn->endpoints[0].x;
268 p2.x = conn->endpoints[1].x;
269 p1.y = p2.y = conn->endpoints[0].y - VERGENT_LINE_WIDTH;
270 renderer_ops->draw_line(renderer,&p1,&p2,&color_black);
271 p1.y = p2.y = conn->endpoints[0].y + VERGENT_LINE_WIDTH;
272 renderer_ops->draw_line(renderer,&p1,&p2,&color_black);
273 break;
274 }
275 }
276
277 static void
vergent_update_data(Vergent * vergent)278 vergent_update_data(Vergent *vergent)
279 {
280 Connection *conn = &vergent->connection;
281 LineBBExtras *extra = &conn->extra_spacing;
282 DiaObject *obj = &conn->object;
283 Point p0,p1;
284
285 conn->endpoints[1].y = conn->endpoints[0].y;
286 if (ABS(conn->endpoints[1].x-conn->endpoints[0].x) < 3.0)
287 conn->endpoints[1].x = conn->endpoints[0].x + 3.0;
288
289 obj->position = conn->endpoints[0];
290
291 p0.x = conn->endpoints[0].x + 1.0;
292 p1.x = conn->endpoints[1].x - 1.0;
293 p0.y = p1.y = conn->endpoints[0].y;
294
295 switch(vergent->type) {
296 case VERGENT_OR:
297 extra->start_trans =
298 extra->start_long =
299 extra->end_trans =
300 extra->end_long = VERGENT_LINE_WIDTH/2.0;
301 connection_update_boundingbox(conn);
302
303 /* place the connection point lines */
304 connpointline_update(vergent->north);
305 connpointline_putonaline(vergent->north,&p0,&p1);
306 vergent->northwest.pos = p0;
307 vergent->northwest.directions = DIR_NORTH;
308 vergent->northeast.pos = p1;
309 vergent->northeast.directions = DIR_NORTH;
310 connpointline_update(vergent->south);
311 connpointline_putonaline(vergent->south,&p0,&p1);
312 vergent->southwest.pos = p0;
313 vergent->southwest.directions = DIR_SOUTH;
314 vergent->southeast.pos = p1;
315 vergent->southeast.directions = DIR_SOUTH;
316 break;
317 case VERGENT_AND:
318 extra->start_trans =
319 extra->end_trans = (3 * VERGENT_LINE_WIDTH)/2.0;
320 extra->start_long =
321 extra->end_long = VERGENT_LINE_WIDTH/2.0;
322 connection_update_boundingbox(conn);
323 connection_update_boundingbox(conn);
324
325 /* place the connection point lines */
326 p0.y = p1.y = p0.y - VERGENT_LINE_WIDTH;
327 connpointline_update(vergent->north);
328 connpointline_putonaline(vergent->north,&p0,&p1);
329 vergent->northwest.pos = p0;
330 vergent->northwest.directions = DIR_NORTH;
331 vergent->northeast.pos = p1;
332 vergent->northeast.directions = DIR_NORTH;
333 p0.y = p1.y = p0.y + 2.0 *VERGENT_LINE_WIDTH;
334 connpointline_update(vergent->south);
335 connpointline_putonaline(vergent->south,&p0,&p1);
336 vergent->southwest.pos = p0;
337 vergent->southwest.directions = DIR_SOUTH;
338 vergent->southeast.pos = p1;
339 vergent->southeast.directions = DIR_SOUTH;
340 break;
341 }
342 connection_update_handles(conn);
343 }
344
345 /* DiaObject menu handling */
346
347 typedef struct {
348 ObjectChange obj_change;
349
350 ObjectChange *north,*south;
351 } VergentChange;
352
vergent_change_apply(VergentChange * change,DiaObject * obj)353 static void vergent_change_apply(VergentChange *change, DiaObject *obj)
354 {
355 change->north->apply(change->north,obj);
356 change->south->apply(change->south,obj);
357 }
358
vergent_change_revert(VergentChange * change,DiaObject * obj)359 static void vergent_change_revert(VergentChange *change, DiaObject *obj)
360 {
361 change->north->revert(change->north,obj);
362 change->south->revert(change->south,obj);
363 }
364
vergent_change_free(VergentChange * change)365 static void vergent_change_free(VergentChange *change)
366 {
367 if (change->north->free) change->north->free(change->north);
368 g_free(change->north);
369 if (change->south->free) change->south->free(change->south);
370 g_free(change->south);
371 }
372
373 static ObjectChange *
vergent_create_change(Vergent * vergent,int add,Point * clicked)374 vergent_create_change(Vergent *vergent, int add, Point *clicked)
375 {
376 VergentChange *vc;
377
378 vc = g_new0(VergentChange,1);
379 vc->obj_change.apply = (ObjectChangeApplyFunc)vergent_change_apply;
380 vc->obj_change.revert = (ObjectChangeRevertFunc)vergent_change_revert;
381 vc->obj_change.free = (ObjectChangeFreeFunc)vergent_change_free;
382
383 if (add) {
384 vc->north = connpointline_add_point(vergent->north,clicked);
385 vc->south = connpointline_add_point(vergent->south,clicked);
386 } else {
387 vc->north = connpointline_remove_point(vergent->north,clicked);
388 vc->south = connpointline_remove_point(vergent->south,clicked);
389 }
390 vergent_update_data(vergent);
391 return (ObjectChange *)vc;
392 }
393
394 static ObjectChange *
vergent_add_cp_callback(DiaObject * obj,Point * clicked,gpointer data)395 vergent_add_cp_callback(DiaObject *obj, Point *clicked, gpointer data)
396 {
397 return vergent_create_change((Vergent *)obj,1,clicked);
398 }
399
400 static ObjectChange *
vergent_delete_cp_callback(DiaObject * obj,Point * clicked,gpointer data)401 vergent_delete_cp_callback(DiaObject *obj, Point *clicked, gpointer data)
402 {
403 return vergent_create_change((Vergent *)obj,0,clicked);
404 }
405
406 static DiaMenuItem object_menu_items[] = {
407 { N_("Add connection point"), vergent_add_cp_callback, NULL, 1 },
408 { N_("Delete connection point"), vergent_delete_cp_callback, NULL, 1 },
409 };
410
411 static DiaMenu object_menu = {
412 N_("GRAFCET OR/AND vergent"),
413 sizeof(object_menu_items)/sizeof(DiaMenuItem),
414 object_menu_items,
415 NULL
416 };
417
418 static DiaMenu *
vergent_get_object_menu(Vergent * vergent,Point * clickedpoint)419 vergent_get_object_menu(Vergent *vergent, Point *clickedpoint)
420 {
421 /* Set entries sensitive/selected etc here */
422 g_assert(vergent->north->num_connections == vergent->south->num_connections);
423
424 object_menu_items[0].active = 1;
425 object_menu_items[1].active = (vergent->north->num_connections > 1);
426
427 return &object_menu;
428 }
429
430 static DiaObject *
vergent_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)431 vergent_create(Point *startpoint,
432 void *user_data,
433 Handle **handle1,
434 Handle **handle2)
435 {
436 Vergent *vergent;
437 Connection *conn;
438 DiaObject *obj;
439 int i;
440 Point defaultlen = {6.0,0.0};
441
442 vergent = g_malloc0(sizeof(Vergent));
443 conn = &vergent->connection;
444 obj = &conn->object;
445
446 obj->type = &vergent_type;
447 obj->ops = &vergent_ops;
448
449 conn->endpoints[0] = *startpoint;
450 conn->endpoints[1] = *startpoint;
451 point_add(&conn->endpoints[1], &defaultlen);
452
453 connection_init(conn, 2, 4);
454
455 obj->connections[0] = &vergent->northeast;
456 obj->connections[1] = &vergent->northwest;
457 obj->connections[2] = &vergent->southwest;
458 obj->connections[3] = &vergent->southeast;
459 for (i=0; i<4; i++) {
460 obj->connections[i]->object = obj;
461 obj->connections[i]->connected = NULL;
462 }
463
464 vergent->north = connpointline_create(obj,1);
465 vergent->south = connpointline_create(obj,1);
466
467 switch(GPOINTER_TO_INT(user_data)) {
468 case VERGENT_OR:
469 case VERGENT_AND:
470 vergent->type = GPOINTER_TO_INT(user_data);
471 break;
472 default:
473 g_warning("in vergent_create(): incorrect user_data %p",user_data);
474 vergent->type = VERGENT_OR;
475 }
476
477 vergent_update_data(vergent);
478
479 *handle1 = &conn->endpoint_handles[0];
480 *handle2 = &conn->endpoint_handles[1];
481
482 return &vergent->connection.object;
483 }
484
485 static void
vergent_destroy(Vergent * vergent)486 vergent_destroy(Vergent *vergent)
487 {
488 connpointline_destroy(vergent->south);
489 connpointline_destroy(vergent->north);
490 connection_destroy(&vergent->connection);
491 }
492
493 static DiaObject *
vergent_load(ObjectNode obj_node,int version,const char * filename)494 vergent_load(ObjectNode obj_node, int version, const char *filename)
495 {
496 return object_load_using_properties(&vergent_type,
497 obj_node,version,filename);
498 }
499
500
501
502
503
504
505
506
507
508
509
510
511