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