2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / FS / flow-poly.c
blobe16a1a061a0bca77dd7b3cd9a1b7eb6b74a2520c
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998, 1999 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <assert.h>
24 #include <math.h>
25 #include <string.h>
26 #include <stdio.h>
28 #include "intl.h"
29 #include "object.h"
30 #include "connection.h"
31 #include "diarenderer.h"
32 #include "handle.h"
33 #include "arrows.h"
34 #include "diamenu.h"
35 #include "text.h"
37 #include "pixmaps/flow.xpm"
39 Color flow_color_energy = { 1.0f, 0.0f, 0.0f };
40 Color flow_color_material = { 0.8f, 0.0f, 0.8f };
41 Color flow_color_signal = { 0.0f, 0.0f, 1.0f };
43 typedef struct _Flow Flow;
45 typedef enum {
46 FLOW_ENERGY,
47 FLOW_MATERIAL,
48 FLOW_SIGNAL
49 } FlowType;
51 struct _Flow {
52 Connection connection;
54 Handle text_handle;
56 Text* text;
57 FlowType type;
60 #define FLOW_WIDTH 0.1
61 #define FLOW_MATERIAL_WIDTH 0.2
62 #define FLOW_DASHLEN 0.4
63 #define FLOW_FONTHEIGHT 0.6
64 #define FLOW_ARROWLEN 0.8
65 #define FLOW_ARROWWIDTH 0.5
66 #define HANDLE_MOVE_TEXT (HANDLE_CUSTOM1)
68 static ObjectChange* flow_move_handle(Flow *flow, Handle *handle,
69 Point *to, ConnectionPoint *cp,
70 HandleMoveReason reason,
71 ModifierKeys modifiers);
72 static ObjectChange* flow_move(Flow *flow, Point *to);
73 static void flow_select(Flow *flow, Point *clicked_point,
74 DiaRenderer *interactive_renderer);
75 static void flow_draw(Flow *flow, DiaRenderer *renderer);
76 static DiaObject *flow_create(Point *startpoint,
77 void *user_data,
78 Handle **handle1,
79 Handle **handle2);
80 static real flow_distance_from(Flow *flow, Point *point);
81 static void flow_update_data(Flow *flow);
82 static void flow_destroy(Flow *flow);
83 static DiaObject *flow_copy(Flow *flow);
84 static void flow_save(Flow *flow, ObjectNode obj_node,
85 const char *filename);
86 static DiaObject *flow_load(ObjectNode obj_node, int version,
87 const char *filename);
88 static DiaMenu *flow_get_object_menu(Flow *flow, Point *clickedpoint) ;
91 static ObjectTypeOps flow_type_ops =
93 (CreateFunc) flow_create,
94 (LoadFunc) flow_load,
95 (SaveFunc) flow_save,
96 (GetDefaultsFunc) NULL,
97 (ApplyDefaultsFunc) NULL,
98 } ;
100 DiaObjectType flow_type =
102 "FS - Flow", /* name */
103 0, /* version */
104 (char **) flow_xpm, /* pixmap */
105 &flow_type_ops /* ops */
108 static ObjectOps flow_ops = {
109 (DestroyFunc) flow_destroy,
110 (DrawFunc) flow_draw,
111 (DistanceFunc) flow_distance_from,
112 (SelectFunc) flow_select,
113 (CopyFunc) flow_copy,
114 (MoveFunc) flow_move,
115 (MoveHandleFunc) flow_move_handle,
116 (GetPropertiesFunc) object_create_props_dialog,
117 (ApplyPropertiesFunc) object_apply_props_from_dialog,
118 (ObjectMenuFunc) flow_get_object_menu,
119 (DescribePropsFunc) flow_describe_props,
120 (GetPropsFunc) flow_get_props,
121 (SetPropsFunc) flow_set_props,
124 static PropEnumData prop_flow_type_data[] = {
125 { N_("Energy"),FLOW_ENERGY },
126 { N_("Material"),FLOW_MATERIAL },
127 { N_("Signal"),FLOW_SIGNAL },
128 { NULL, 0 }
131 static PropDescription flow_props[] = {
132 OBJECT_COMMON_PROPERTIES,
133 { "type", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
134 N_("Type:"), NULL, prop_flow_type_data },
135 { "text", PROP_TYPE_TEXT, PROP_FLAG_VISIBLE, NULL, NULL },
136 PROP_STD_TEXT_ALIGNMENT,
137 PROP_STD_TEXT_FONT,
138 PROP_STD_TEXT_HEIGHT,
139 /* Colour determined from type, don't show */
140 { "text_colour", PROP_TYPE_COLOUR, PROP_FLAG_DONT_SAVE, },
141 PROP_DESC_END
144 static PropDescription *
145 flow_describe_props(Flow *mes)
147 if (flow_props[0].quark == 0)
148 prop_desc_list_calculate_quarks(flow_props);
149 return flow_props;
153 static PropOffset flow_offsets[] = {
154 OBJECT_COMMON_PROPERTIES_OFFSETS,
155 { "type", PROP_TYPE_ENUM, offsetof(Flow, type) },
156 { "text", PROP_TYPE_TEXT, offsetof (Flow, text) },
157 { "text_alignment", PROP_TYPE_ENUM, offsetof (Flow, attrs.alignment) },
158 { "text_font", PROP_TYPE_FONT, offsetof (Flow, attrs.font) },
159 { "text_height", PROP_TYPE_REAL, offsetof (Flow, attrs.height) },
160 { "text_colour", PROP_TYPE_COLOUR, offsetof (Flow, attrs.color) },
161 { NULL, 0, 0 }
164 static void
165 flow_get_props(Flow * flow, GPtrArray *props)
167 text_get_attributes (flow->text, &flow->attrs);
168 object_get_props_from_offsets(&flow->connection.object,
169 flow_offsets, props);
172 static void
173 flow_set_props(Flow *flow, GPtrArray *props)
175 object_set_props_from_offsets(&flow->connection.object,
176 flow_offsets, props);
177 apply_textattr_properties (props, flow->text, "text", &flow->attrs);
178 flow_update_data(flow);
181 static real
182 flow_distance_from(Flow *flow, Point *point)
184 Point *endpoints;
185 real linedist;
186 real textdist;
188 endpoints = &flow->connection.endpoints[0];
190 linedist = distance_line_point(&endpoints[0], &endpoints[1],
191 flow->type == FLOW_MATERIAL ? FLOW_MATERIAL_WIDTH : FLOW_WIDTH,
192 point);
193 textdist = text_distance_from( flow->text, point ) ;
195 return linedist > textdist ? textdist : linedist ;
198 static void
199 flow_select(Flow *flow, Point *clicked_point,
200 DiaRenderer *interactive_renderer)
202 text_set_cursor(flow->text, clicked_point, interactive_renderer);
203 text_grab_focus(flow->text, &flow->connection.object);
205 connection_update_handles(&flow->connection);
208 static ObjectChange*
209 flow_move_handle(Flow *flow, Handle *handle,
210 Point *to, ConnectionPoint *cp,
211 HandleMoveReason reason, ModifierKeys modifiers)
213 Point p1, p2;
214 Point *endpoints;
216 assert(flow!=NULL);
217 assert(handle!=NULL);
218 assert(to!=NULL);
220 if (handle->id == HANDLE_MOVE_TEXT) {
221 flow->text->position = *to;
222 } else {
223 real dest_length ;
224 real orig_length2 ;
225 real along_mag, norm_mag ;
226 Point along ;
228 endpoints = &flow->connection.endpoints[0];
229 p1 = flow->text->position ;
230 point_sub( &p1, &endpoints[0] ) ;
232 p2 = endpoints[1] ;
233 point_sub( &p2, &endpoints[0] ) ;
234 orig_length2= point_dot( &p2, &p2 ) ;
235 along = p2 ;
236 if ( orig_length2 > 1e-5 ) {
237 along_mag = point_dot( &p2, &p1 )/sqrt(orig_length2) ;
238 along_mag *= along_mag ;
239 norm_mag = point_dot( &p1, &p1 ) ;
240 norm_mag = (real)sqrt( (double)( norm_mag - along_mag ) ) ;
241 along_mag = (real)sqrt( along_mag/orig_length2 ) ;
242 if ( p1.x*p2.y - p1.y*p2.x > 0.0 )
243 norm_mag = -norm_mag ;
244 } else {
245 along_mag = 0.5 ;
246 norm_mag = (real)sqrt( (double) point_dot( &p1, &p1 ) ) ;
249 connection_move_handle(&flow->connection, handle->id, to, cp,
250 reason, modifiers);
252 p2 = endpoints[1] ;
253 point_sub( &p2, &endpoints[0] ) ;
254 flow->text->position = endpoints[0] ;
255 along = p2 ;
256 p2.x = -along.y ;
257 p2.y = along.x ;
258 dest_length = point_dot( &p2, &p2 ) ;
259 if ( dest_length > 1e-5 ) {
260 point_normalize( &p2 ) ;
261 } else {
262 p2.x = 0.0 ; p2.y = -1.0 ;
264 point_scale( &p2, norm_mag ) ;
265 point_scale( &along, along_mag ) ;
266 point_add( &flow->text->position, &p2 ) ;
267 point_add( &flow->text->position, &along ) ;
270 flow_update_data(flow);
272 return NULL;
275 static ObjectChange*
276 flow_move(Flow *flow, Point *to)
278 Point start_to_end;
279 Point *endpoints = &flow->connection.endpoints[0];
280 Point delta;
282 delta = *to;
283 point_sub(&delta, &endpoints[0]);
285 start_to_end = endpoints[1];
286 point_sub(&start_to_end, &endpoints[0]);
288 endpoints[1] = endpoints[0] = *to;
289 point_add(&endpoints[1], &start_to_end);
291 point_add(&flow->text->position, &delta);
293 flow_update_data(flow);
295 return NULL;
298 static void
299 flow_draw(Flow *flow, DiaRenderer *renderer)
301 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
302 Point *endpoints, p1, p2, px;
303 ArrowType arrow_type;
304 int n1 = 1, n2 = 0;
305 char *mname;
306 Color* render_color ;
307 Arrow arrow;
309 assert(flow != NULL);
310 assert(renderer != NULL);
312 endpoints = &flow->connection.endpoints[0];
314 renderer_ops->set_linewidth(renderer, FLOW_WIDTH);
316 renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
319 switch (flow->type) {
320 case FLOW_SIGNAL:
321 renderer_ops->set_dashlength(renderer, FLOW_DASHLEN);
322 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
323 render_color = &flow_color_signal ;
324 break ;
325 case FLOW_MATERIAL:
326 renderer_ops->set_linewidth(renderer, FLOW_MATERIAL_WIDTH ) ;
327 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
328 render_color = &flow_color_material ;
329 break ;
330 case FLOW_ENERGY:
331 render_color = &flow_color_energy ;
332 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
335 p1 = endpoints[n1];
336 p2 = endpoints[n2];
338 arrow.type = ARROW_FILLED_TRIANGLE;
339 arrow.length = FLOW_ARROWLEN;
340 arrow.width = FLOW_ARROWWIDTH;
342 renderer_ops->draw_line_with_arrows(renderer,
343 &p1, &p2, FLOW_WIDTH,
344 render_color,
345 &arrow);
347 renderer_ops->set_font(renderer, flow_font,
348 FLOW_FONTHEIGHT);
350 text_draw(flow->text, renderer);
353 static DiaObject *
354 flow_create(Point *startpoint,
355 void *user_data,
356 Handle **handle1,
357 Handle **handle2)
359 Flow *flow;
360 ConnectionBBExtras *extra;
361 Connection *conn;
362 DiaObject *obj;
363 Point p ;
364 Point n ;
366 flow = g_malloc0(sizeof(Flow));
368 conn = &flow->connection;
369 conn->endpoints[0] = *startpoint;
370 conn->endpoints[1] = *startpoint;
371 conn->endpoints[1].x += 1.5;
373 obj = &conn->object;
374 extra = &conn->extra_spacing;
376 obj->type = &flow_type;
377 obj->ops = &flow_ops;
379 connection_init(conn, 3, 0);
381 p = conn->endpoints[1] ;
382 point_sub( &p, &conn->endpoints[0] ) ;
383 point_scale( &p, 0.5 ) ;
384 n.x = p.y ;
385 n.y = -p.x ;
386 if ( fabs(n.x) < 1.e-5 && fabs(n.y) < 1.e-5 ) {
387 n.x = 0. ;
388 n.y = -1. ;
389 } else {
390 point_normalize( &n ) ;
392 point_scale( &n, 0.5*FLOW_FONTHEIGHT ) ;
393 point_add( &p, &n ) ;
394 point_add( &p, &conn->endpoints[0] ) ;
395 flow->textpos = p;
397 flow->text_handle.id = HANDLE_MOVE_TEXT;
398 flow->text_handle.type = HANDLE_MINOR_CONTROL;
399 flow->text_handle.connect_type = HANDLE_NONCONNECTABLE;
400 flow->text_handle.connected_to = NULL;
401 obj->handles[2] = &flow->text_handle;
403 extra->start_trans =
404 extra->start_long =
405 extra->end_long = FLOW_WIDTH/2.0;
406 extra->end_trans = MAX(FLOW_WIDTH,FLOW_ARROWLEN)/2.0;
408 flow_update_data(flow);
410 *handle1 = obj->handles[0];
411 *handle2 = obj->handles[1];
412 return &flow->connection.object;
416 static void
417 flow_destroy(Flow *flow)
419 connection_destroy(&flow->connection);
420 text_destroy(flow->text) ;
423 static DiaObject *
424 flow_copy(Flow *flow)
426 Flow *newflow;
427 Connection *conn, *newconn;
428 DiaObject *newobj;
430 conn = &flow->connection;
432 newflow = g_malloc0(sizeof(Flow));
433 newconn = &newflow->connection;
434 newobj = &newconn->object;
436 connection_copy(conn, newconn);
438 newflow->text_handle = flow->text_handle;
439 newobj->handles[2] = &newflow->text_handle;
441 newflow->text = text_copy(flow->text);
442 newflow->type = flow->type;
444 return &newflow->connection.object;
448 static void
449 flow_update_data(Flow *flow)
451 Connection *conn = &flow->connection;
452 DiaObject *obj = &conn->object;
453 Rectangle rect;
454 Color* color ;
456 obj->position = conn->endpoints[0];
458 switch (flow->type) {
459 case FLOW_ENERGY:
460 color = &flow_color_energy ;
461 break ;
462 case FLOW_MATERIAL:
463 color = &flow_color_material ;
464 break ;
465 case FLOW_SIGNAL:
466 color = &flow_color_signal ;
467 break ;
469 text_set_color( flow->text, color ) ;
471 flow->text_handle.pos = flow->text->position;
473 connection_update_handles(conn);
475 /* Boundingbox: */
476 connection_update_boundingbox(conn);
478 /* Add boundingbox for text: */
479 text_calc_boundingbox(flow->text, &rect) ;
480 rectangle_union(&obj->bounding_box, &rect);
484 static void
485 flow_save(Flow *flow, ObjectNode obj_node, const char *filename)
487 connection_save(&flow->connection, obj_node);
489 data_add_text(new_attribute(obj_node, "text"),
490 flow->text) ;
491 data_add_int(new_attribute(obj_node, "type"),
492 flow->type);
495 static DiaObject *
496 flow_load(ObjectNode obj_node, int version, const char *filename)
498 Flow *flow;
499 AttributeNode attr;
500 Connection *conn;
501 DiaObject *obj;
502 ConnectionBBExtras *extra;
504 if (flow_font == NULL) {
505 /* choose default font name for your locale. see also font_data structure
506 in lib/font.c. */
507 flow_font = font_getfont (_("Helvetica-Oblique"));
510 flow = g_malloc0(sizeof(Flow));
512 conn = &flow->connection;
513 obj = &conn->object;
514 extra = &conn->extra_spacing;
516 obj->type = &flow_type;
517 obj->ops = &flow_ops;
519 connection_load(conn, obj_node);
521 connection_init(conn, 3, 0);
523 flow->text = NULL;
524 attr = object_find_attribute(obj_node, "text");
525 if (attr != NULL)
526 flow->text = data_text(attribute_first_data(attr));
528 attr = object_find_attribute(obj_node, "type");
529 if (attr != NULL)
530 flow->type = (FlowType)data_int(attribute_first_data(attr));
532 flow->text_handle.id = HANDLE_MOVE_TEXT;
533 flow->text_handle.type = HANDLE_MINOR_CONTROL;
534 flow->text_handle.connect_type = HANDLE_NONCONNECTABLE;
535 flow->text_handle.connected_to = NULL;
536 obj->handles[2] = &flow->text_handle;
538 extra->start_trans =
539 extra->start_long =
540 extra->end_long = FLOW_WIDTH/2.0;
541 extra->end_trans = MAX(FLOW_WIDTH,FLOW_ARROWLEN)/2.0;
543 flow_update_data(flow);
545 return &flow->connection.object;
548 static ObjectChange *
549 flow_set_type_callback (DiaObject* obj, Point* clicked, gpointer data)
551 ((Flow*)obj)->type = (int) data ;
552 flow_update_defaults( (Flow*) obj, 1 ) ;
553 if ( defaults_dialog )
554 fill_in_defaults_dialog() ;
555 flow_update_data((Flow*)obj);
557 return NULL;
560 static DiaMenuItem flow_menu_items[] = {
561 { N_("Energy"), flow_set_type_callback, (void*)FLOW_ENERGY, 1 },
562 { N_("Material"), flow_set_type_callback, (void*)FLOW_MATERIAL, 1 },
563 { N_("Signal"), flow_set_type_callback, (void*)FLOW_SIGNAL, 1 },
566 static DiaMenu flow_menu = {
567 "Flow",
568 sizeof(flow_menu_items)/sizeof(DiaMenuItem),
569 flow_menu_items,
570 NULL
573 static DiaMenu *
574 flow_get_object_menu(Flow *flow, Point *clickedpoint)
576 /* Set entries sensitive/selected etc here */
577 flow_menu_items[0].active = 1;
578 return &flow_menu;