2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / objects / FS / flow-ortho.c
blobd354202ab175abe4139f052889202d9b05e1ee09
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 /* If you have a problem with the Function Structure (FS) components,
20 * please send e-mail to David Thompson <dcthomp@mail.utexas.edu>
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <assert.h>
28 #include <math.h>
29 #include <string.h>
30 #include <stdio.h>
32 #include "intl.h"
33 #include "object.h"
34 #include "objchange.h"
35 #include "connection.h"
36 #include "diarenderer.h"
37 #include "handle.h"
38 #include "arrows.h"
39 #include "diamenu.h"
40 #include "text.h"
41 #include "orth_conn.h"
42 #include "element.h"
43 #include "properties.h"
45 #include "pixmaps/orthflow.xpm"
48 typedef struct _Orthflow Orthflow;
49 typedef struct _OrthflowChange OrthflowChange;
51 typedef enum {
52 ORTHFLOW_ENERGY,
53 ORTHFLOW_MATERIAL,
54 ORTHFLOW_SIGNAL
55 } OrthflowType;
57 struct _Orthflow {
58 OrthConn orth ;
60 Handle text_handle;
62 Text* text;
63 TextAttributes attrs;
64 OrthflowType type;
65 Point textpos; /* This is the master position, only overridden in load */
68 enum OrthflowChangeType {
69 TEXT_EDIT=1,
70 FLOW_TYPE=2,
71 BOTH=3
74 struct _OrthflowChange {
75 ObjectChange obj_change ;
76 enum OrthflowChangeType change_type ;
77 OrthflowType type ;
78 char* text ;
81 Color orthflow_color_energy = { 1.0f, 0.0f, 0.0f };
82 Color orthflow_color_material = { 0.8f, 0.0f, 0.8f };
83 Color orthflow_color_signal = { 0.0f, 0.0f, 1.0f };
86 #define ORTHFLOW_WIDTH 0.1
87 #define ORTHFLOW_MATERIAL_WIDTH 0.2
88 #define ORTHFLOW_DASHLEN 0.4
89 #define ORTHFLOW_FONTHEIGHT 0.6
90 #define ORTHFLOW_ARROWLEN 0.8
91 #define ORTHFLOW_ARROWWIDTH 0.5
92 #define HANDLE_MOVE_TEXT (HANDLE_CUSTOM2)
94 static DiaFont *orthflow_font = NULL;
96 static ObjectChange* orthflow_move_handle(Orthflow *orthflow, Handle *handle,
97 Point *to, ConnectionPoint *cp,
98 HandleMoveReason reason,
99 ModifierKeys modifiers);
100 static ObjectChange* orthflow_move(Orthflow *orthflow, Point *to);
101 static void orthflow_select(Orthflow *orthflow, Point *clicked_point,
102 DiaRenderer *interactive_renderer);
103 static void orthflow_draw(Orthflow *orthflow, DiaRenderer *renderer);
104 static DiaObject *orthflow_create(Point *startpoint,
105 void *user_data,
106 Handle **handle1,
107 Handle **handle2);
108 static real orthflow_distance_from(Orthflow *orthflow, Point *point);
109 static void orthflow_update_data(Orthflow *orthflow);
110 static void orthflow_destroy(Orthflow *orthflow);
111 static DiaObject *orthflow_copy(Orthflow *orthflow);
112 static PropDescription *orthflow_describe_props(Orthflow *mes);
113 static void
114 orthflow_get_props(Orthflow * orthflow, GPtrArray *props);
115 static void
116 orthflow_set_props(Orthflow * orthflow, GPtrArray *props);
117 static void orthflow_save(Orthflow *orthflow, ObjectNode obj_node,
118 const char *filename);
119 static DiaObject *orthflow_load(ObjectNode obj_node, int version,
120 const char *filename);
121 static DiaMenu *orthflow_get_object_menu(Orthflow *orthflow, Point *clickedpoint) ;
124 static ObjectTypeOps orthflow_type_ops =
126 (CreateFunc) orthflow_create,
127 (LoadFunc) orthflow_load,
128 (SaveFunc) orthflow_save,
129 (GetDefaultsFunc) NULL,
130 (ApplyDefaultsFunc) NULL,
134 DiaObjectType orthflow_type =
136 "FS - Orthflow", /* name */
137 /* Version 0 had no autorouting and so shouldn't have it set by default. */
138 1, /* version */
139 (char **) orthflow_xpm, /* pixmap */
140 &orthflow_type_ops /* ops */
143 static ObjectOps orthflow_ops = {
144 (DestroyFunc) orthflow_destroy,
145 (DrawFunc) orthflow_draw,
146 (DistanceFunc) orthflow_distance_from,
147 (SelectFunc) orthflow_select,
148 (CopyFunc) orthflow_copy,
149 (MoveFunc) orthflow_move,
150 (MoveHandleFunc) orthflow_move_handle,
151 (GetPropertiesFunc) object_create_props_dialog,
152 (ApplyPropertiesFunc) object_apply_props_from_dialog,
153 (ObjectMenuFunc) orthflow_get_object_menu,
154 (DescribePropsFunc) orthflow_describe_props,
155 (GetPropsFunc) orthflow_get_props,
156 (SetPropsFunc) orthflow_set_props,
159 static PropEnumData prop_orthflow_type_data[] = {
160 { N_("Energy"),ORTHFLOW_ENERGY },
161 { N_("Material"),ORTHFLOW_MATERIAL },
162 { N_("Signal"),ORTHFLOW_SIGNAL },
163 { NULL, 0 }
166 static PropDescription orthflow_props[] = {
167 ELEMENT_COMMON_PROPERTIES,
168 { "type", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
169 N_("Type:"), NULL, prop_orthflow_type_data },
170 { "text", PROP_TYPE_TEXT, 0, NULL, NULL },
171 PROP_STD_TEXT_ALIGNMENT,
172 PROP_STD_TEXT_FONT,
173 PROP_STD_TEXT_HEIGHT,
174 /* Colour determined from type, don't show */
175 { "text_colour", PROP_TYPE_COLOUR, PROP_FLAG_DONT_SAVE, },
176 PROP_DESC_END
179 static PropDescription *
180 orthflow_describe_props(Orthflow *mes)
182 if (orthflow_props[0].quark == 0)
183 prop_desc_list_calculate_quarks(orthflow_props);
184 return orthflow_props;
188 static PropOffset orthflow_offsets[] = {
189 OBJECT_COMMON_PROPERTIES_OFFSETS,
190 { "type", PROP_TYPE_ENUM, offsetof(Orthflow, type) },
191 { "text", PROP_TYPE_TEXT, offsetof (Orthflow, text) },
192 { "text_alignment", PROP_TYPE_REAL, offsetof (Orthflow, attrs.alignment) },
193 { "text_font", PROP_TYPE_FONT, offsetof (Orthflow, attrs.font) },
194 { "text_height", PROP_TYPE_REAL, offsetof (Orthflow, attrs.height) },
195 { "text_colour", PROP_TYPE_COLOUR, offsetof (Orthflow, attrs.color) },
196 { NULL, 0, 0 }
199 static void
200 orthflow_get_props(Orthflow * orthflow, GPtrArray *props)
202 text_get_attributes (orthflow->text, &orthflow->attrs);
203 object_get_props_from_offsets(&orthflow->orth.object,
204 orthflow_offsets, props);
207 static void
208 orthflow_set_props(Orthflow *orthflow, GPtrArray *props)
210 object_set_props_from_offsets(&orthflow->orth.object,
211 orthflow_offsets, props);
212 apply_textattr_properties (props, orthflow->text, "text", &orthflow->attrs);
213 orthflow_update_data(orthflow);
218 static void
219 orthflow_change_apply_revert(ObjectChange* objchg, DiaObject* obj)
221 struct _OrthflowChange* change = (struct _OrthflowChange*) objchg ;
222 Orthflow* oflow = (Orthflow*) obj ;
224 if ( change->change_type == FLOW_TYPE || change->change_type == BOTH ) {
225 OrthflowType type = oflow->type ;
226 oflow->type = change->type ;
227 change->type = type ;
228 orthflow_update_data(oflow) ;
231 if ( change->change_type & TEXT_EDIT || change->change_type == BOTH ) {
232 char* tmp = text_get_string_copy( oflow->text ) ;
233 text_set_string( oflow->text, change->text ) ;
234 g_free( change->text ) ;
235 change->text = tmp ;
239 static void
240 orthflow_change_free(ObjectChange* objchg)
242 struct _OrthflowChange* change = (struct _OrthflowChange*) objchg ;
244 if (change->change_type & TEXT_EDIT || change->change_type == BOTH ) {
245 g_free(change->text) ;
249 static ObjectChange*
250 orthflow_create_change( enum OrthflowChangeType change_type,
251 OrthflowType type, Text* text )
253 struct _OrthflowChange* change ;
254 change = g_new0( struct _OrthflowChange, 1 ) ;
255 change->obj_change.apply = (ObjectChangeApplyFunc) orthflow_change_apply_revert ;
256 change->obj_change.revert = (ObjectChangeRevertFunc) orthflow_change_apply_revert ;
257 change->obj_change.free = (ObjectChangeFreeFunc) orthflow_change_free ;
258 change->change_type = change_type ;
260 change->type = type ;
261 if ( text ) {
262 change->text = text_get_string_copy( text ) ;
265 return (ObjectChange*) change ;
268 static real
269 orthflow_distance_from(Orthflow *orthflow, Point *point)
271 real linedist;
272 real textdist;
274 linedist = orthconn_distance_from( &orthflow->orth, point,
275 orthflow->type == ORTHFLOW_MATERIAL ?
276 ORTHFLOW_MATERIAL_WIDTH :
277 ORTHFLOW_WIDTH ) ;
278 textdist = text_distance_from( orthflow->text, point ) ;
280 return linedist > textdist ? textdist : linedist ;
283 static void
284 orthflow_select(Orthflow *orthflow, Point *clicked_point,
285 DiaRenderer *interactive_renderer)
287 text_set_cursor(orthflow->text, clicked_point, interactive_renderer);
288 text_grab_focus(orthflow->text, &orthflow->orth.object);
290 orthconn_update_data(&orthflow->orth);
293 static ObjectChange*
294 orthflow_move_handle(Orthflow *orthflow, Handle *handle,
295 Point *to, ConnectionPoint *cp,
296 HandleMoveReason reason, ModifierKeys modifiers)
298 ObjectChange *change = NULL;
299 assert(orthflow!=NULL);
300 assert(handle!=NULL);
301 assert(to!=NULL);
303 if (handle->id == HANDLE_MOVE_TEXT) {
304 orthflow->textpos = *to;
305 } else {
306 Point along ;
308 along = orthflow->textpos ;
309 point_sub( &along, &(orthconn_get_middle_handle(&orthflow->orth)->pos) ) ;
311 change = orthconn_move_handle( &orthflow->orth, handle, to, cp,
312 reason, modifiers);
313 orthconn_update_data( &orthflow->orth ) ;
315 orthflow->textpos = orthconn_get_middle_handle(&orthflow->orth)->pos ;
316 point_add( &orthflow->textpos, &along ) ;
319 orthflow_update_data(orthflow);
321 return change;
324 static ObjectChange*
325 orthflow_move(Orthflow *orthflow, Point *to)
327 ObjectChange *change;
329 Point *points = &orthflow->orth.points[0];
330 Point delta;
332 delta = *to;
333 point_sub(&delta, &points[0]);
334 point_add(&orthflow->textpos, &delta);
336 change = orthconn_move( &orthflow->orth, to ) ;
338 orthflow_update_data(orthflow);
340 return change;
343 static void
344 orthflow_draw(Orthflow *orthflow, DiaRenderer *renderer)
346 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
347 int n = orthflow->orth.numpoints ;
348 Color* render_color = &orthflow_color_signal;
349 Point *points;
350 real linewidth;
351 Arrow arrow;
353 assert(orthflow != NULL);
354 assert(renderer != NULL);
356 arrow.type = ARROW_FILLED_TRIANGLE;
357 arrow.width = ORTHFLOW_ARROWWIDTH;
358 arrow.length = ORTHFLOW_ARROWLEN;
360 points = &orthflow->orth.points[0];
362 renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
364 switch (orthflow->type) {
365 case ORTHFLOW_SIGNAL:
366 linewidth = ORTHFLOW_WIDTH;
367 renderer_ops->set_dashlength(renderer, ORTHFLOW_DASHLEN);
368 renderer_ops->set_linestyle(renderer, LINESTYLE_DASHED);
369 render_color = &orthflow_color_signal ;
370 break ;
371 case ORTHFLOW_MATERIAL:
372 linewidth = ORTHFLOW_MATERIAL_WIDTH;
373 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
374 render_color = &orthflow_color_material ;
375 break ;
376 case ORTHFLOW_ENERGY:
377 linewidth = ORTHFLOW_WIDTH;
378 renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
379 render_color = &orthflow_color_energy ;
380 break ;
381 default:
382 linewidth = 0.001;
383 break;
386 renderer_ops->set_linewidth(renderer, linewidth);
387 renderer_ops->draw_polyline_with_arrows(renderer, points, n,
388 ORTHFLOW_WIDTH,
389 render_color,
390 NULL, &arrow);
392 renderer_ops->set_font(renderer, orthflow_font,
393 ORTHFLOW_FONTHEIGHT);
395 text_draw(orthflow->text, renderer);
398 static DiaObject *
399 orthflow_create(Point *startpoint,
400 void *user_data,
401 Handle **handle1,
402 Handle **handle2)
404 Orthflow *orthflow;
405 OrthConn *orth;
406 DiaObject *obj;
407 Point p;
408 PolyBBExtras *extra;
409 DiaFont *font;
411 orthflow = g_new0(Orthflow,1);
412 orth = &orthflow->orth ;
413 orthconn_init( orth, startpoint ) ;
415 obj = &orth->object;
416 extra = &orth->extra_spacing;
418 obj->type = &orthflow_type;
419 obj->ops = &orthflow_ops;
421 /* Where to put the text */
422 p = *startpoint ;
423 p.y += 0.1 * ORTHFLOW_FONTHEIGHT ;
424 orthflow->textpos = p;
425 font = dia_font_new_from_style(DIA_FONT_SANS, 0.8);
427 orthflow->text = new_text("", font, 0.8, &p, &color_black, ALIGN_CENTER);
428 dia_font_unref(font);
429 text_get_attributes(orthflow->text, &orthflow->attrs);
431 #if 0
432 if ( orthflow_default_label ) {
433 orthflow->text = text_copy( orthflow_default_label ) ;
434 text_set_position( orthflow->text, &p ) ;
435 } else {
436 Color* color = &orthflow_color_signal;
438 if (orthflow_font == NULL) {
439 orthflow_font = dia_font_new_from_style(DIA_FONT_SANS|DIA_FONT_ITALIC,
440 1.0);
443 switch (orthflow->type) {
444 case ORTHFLOW_ENERGY:
445 color = &orthflow_color_energy ;
446 break ;
447 case ORTHFLOW_MATERIAL:
448 color = &orthflow_color_material ;
449 break ;
450 case ORTHFLOW_SIGNAL:
451 color = &orthflow_color_signal ;
452 break ;
455 orthflow->text = new_text("", orthflow_font, ORTHFLOW_FONTHEIGHT,
456 &p, color, ALIGN_CENTER);
458 #endif
460 orthflow->text_handle.id = HANDLE_MOVE_TEXT;
461 orthflow->text_handle.type = HANDLE_MINOR_CONTROL;
462 orthflow->text_handle.connect_type = HANDLE_NONCONNECTABLE;
463 orthflow->text_handle.connected_to = NULL;
464 object_add_handle( obj, &orthflow->text_handle ) ;
466 extra->start_long =
467 extra->start_trans =
468 extra->middle_trans = ORTHFLOW_WIDTH/2.0;
469 extra->end_long =
470 extra->end_trans = ORTHFLOW_WIDTH/2 + ORTHFLOW_ARROWLEN;
472 orthflow_update_data(orthflow);
474 /*obj->handles[1] = orth->handles[2] ;*/
475 *handle1 = obj->handles[0];
476 *handle2 = obj->handles[1];
477 return &orthflow->orth.object;
481 static void
482 orthflow_destroy(Orthflow *orthflow)
484 orthconn_destroy( &orthflow->orth ) ;
485 text_destroy( orthflow->text ) ;
488 static DiaObject *
489 orthflow_copy(Orthflow *orthflow)
491 Orthflow *neworthflow;
492 OrthConn *orth, *neworth;
493 DiaObject *newobj;
495 orth = &orthflow->orth;
497 neworthflow = g_malloc0(sizeof(Orthflow));
498 neworth = &neworthflow->orth;
499 newobj = &neworth->object;
501 orthconn_copy(orth, neworth);
503 neworthflow->text_handle = orthflow->text_handle;
504 newobj->handles[orth->numpoints-1] = &neworthflow->text_handle;
506 neworthflow->text = text_copy(orthflow->text);
507 neworthflow->type = orthflow->type;
509 return &neworthflow->orth.object;
513 static void
514 orthflow_update_data(Orthflow *orthflow)
516 OrthConn *orth = &orthflow->orth ;
517 DiaObject *obj = &orth->object;
518 Rectangle rect;
519 Color* color = &orthflow_color_signal;
521 switch (orthflow->type) {
522 case ORTHFLOW_ENERGY:
523 color = &orthflow_color_energy ;
524 break ;
525 case ORTHFLOW_MATERIAL:
526 color = &orthflow_color_material ;
527 break ;
528 case ORTHFLOW_SIGNAL:
529 color = &orthflow_color_signal ;
530 break ;
532 text_set_color( orthflow->text, color ) ;
534 text_set_position( orthflow->text, &orthflow->textpos ) ;
535 orthflow->text_handle.pos = orthflow->textpos;
537 orthconn_update_data(orth);
538 obj->position = orth->points[0];
540 /* Boundingbox: */
541 orthconn_update_boundingbox(orth);
543 /* Add boundingbox for text: */
544 text_calc_boundingbox(orthflow->text, &rect) ;
545 rectangle_union(&obj->bounding_box, &rect);
549 static void
550 orthflow_save(Orthflow *orthflow, ObjectNode obj_node, const char *filename)
552 orthconn_save(&orthflow->orth, obj_node);
554 data_add_text(new_attribute(obj_node, "text"),
555 orthflow->text) ;
556 data_add_int(new_attribute(obj_node, "type"),
557 orthflow->type);
560 static DiaObject *
561 orthflow_load(ObjectNode obj_node, int version, const char *filename)
563 Orthflow *orthflow;
564 AttributeNode attr;
565 OrthConn *orth;
566 DiaObject *obj;
567 PolyBBExtras *extra;
569 if (orthflow_font == NULL) {
570 orthflow_font = dia_font_new_from_style(DIA_FONT_SANS|DIA_FONT_ITALIC,
571 1.0);
574 orthflow = g_malloc0(sizeof(Orthflow));
576 orth = &orthflow->orth;
577 obj = &orth->object;
578 extra = &orth->extra_spacing;
580 obj->type = &orthflow_type;
581 obj->ops = &orthflow_ops;
583 orthconn_load(orth, obj_node);
585 orthflow->text = NULL;
586 attr = object_find_attribute(obj_node, "text");
587 if (attr != NULL)
588 orthflow->text = data_text(attribute_first_data(attr));
590 attr = object_find_attribute(obj_node, "type");
591 if (attr != NULL)
592 orthflow->type = (OrthflowType)data_int(attribute_first_data(attr));
594 orthflow->text_handle.id = HANDLE_MOVE_TEXT;
595 orthflow->text_handle.type = HANDLE_MINOR_CONTROL;
596 orthflow->text_handle.connect_type = HANDLE_NONCONNECTABLE;
597 orthflow->text_handle.connected_to = NULL;
598 /* Mein Gott! The extra handle was never added */
599 object_add_handle(obj, &orthflow->text_handle);
600 obj->handles[orth->numpoints-1] = &orthflow->text_handle;
602 extra->start_long =
603 extra->start_trans =
604 extra->middle_trans = ORTHFLOW_WIDTH/2.0;
605 extra->end_long =
606 extra->end_trans = ORTHFLOW_WIDTH/2 + ORTHFLOW_ARROWLEN;
607 orthflow->textpos = orthflow->text->position;
609 orthflow_update_data(orthflow);
611 return &orthflow->orth.object;
614 static ObjectChange *
615 orthflow_set_type_callback (DiaObject* obj, Point* clicked, gpointer data)
617 ObjectChange* change ;
618 change = orthflow_create_change( FLOW_TYPE, ((Orthflow*)obj)->type, 0 ) ;
620 ((Orthflow*)obj)->type = (int) data ;
621 orthflow_update_data((Orthflow*)obj);
623 return change;
626 static ObjectChange *
627 orthflow_segment_callback (DiaObject* obj, Point* clicked, gpointer data)
629 if ( (int)data )
630 return orthconn_add_segment( (OrthConn*)obj, clicked ) ;
632 return orthconn_delete_segment( (OrthConn*)obj, clicked ) ;
635 static DiaMenuItem orthflow_menu_items[] = {
636 { N_("Energy"), orthflow_set_type_callback, (void*)ORTHFLOW_ENERGY, 1 },
637 { N_("Material"), orthflow_set_type_callback, (void*)ORTHFLOW_MATERIAL, 1 },
638 { N_("Signal"), orthflow_set_type_callback, (void*)ORTHFLOW_SIGNAL, 1 },
639 { N_("Add segment"), orthflow_segment_callback, (void*)1, 1 },
640 { N_("Delete segment"), orthflow_segment_callback, (void*)0, 1 },
641 ORTHCONN_COMMON_MENUS,
644 static DiaMenu orthflow_menu = {
645 "Orthflow",
646 sizeof(orthflow_menu_items)/sizeof(DiaMenuItem),
647 orthflow_menu_items,
648 NULL
651 static DiaMenu *
652 orthflow_get_object_menu(Orthflow *orthflow, Point *clickedpoint)
654 orthflow_menu_items[4].active = orthflow->orth.numpoints > 3;
655 orthconn_update_object_menu((OrthConn*)orthflow,
656 clickedpoint, &orthflow_menu_items[5]);
657 return &orthflow_menu;