1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 * Copyright (C) 2002 David Hoover
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include "connection.h"
30 #include "connectionpoint.h"
31 #include "diarenderer.h"
32 #include "attributes.h"
35 #include "connpoint_line.h"
36 #include "properties.h"
38 #include "tool-icons.h"
40 #define DEFAULT_WIDTH 0.25
42 typedef struct _LineProperties LineProperties
;
44 typedef struct _Line
{
45 Connection connection
;
52 Arrow start_arrow
, end_arrow
;
54 real absolute_start_gap
, absolute_end_gap
;
57 struct _LineProperties
{
58 real absolute_start_gap
, absolute_end_gap
;
61 static LineProperties default_properties
;
63 static ObjectChange
* line_move_handle(Line
*line
, Handle
*handle
,
64 Point
*to
, ConnectionPoint
*cp
,
65 HandleMoveReason reason
,
66 ModifierKeys modifiers
);
67 static ObjectChange
* line_move(Line
*line
, Point
*to
);
68 static void line_select(Line
*line
, Point
*clicked_point
,
69 DiaRenderer
*interactive_renderer
);
70 static void line_draw(Line
*line
, DiaRenderer
*renderer
);
71 static DiaObject
*line_create(Point
*startpoint
,
75 static real
line_distance_from(Line
*line
, Point
*point
);
76 static void line_update_data(Line
*line
);
77 static void line_destroy(Line
*line
);
78 static DiaObject
*line_copy(Line
*line
);
80 static PropDescription
*line_describe_props(Line
*line
);
81 static void line_get_props(Line
*line
, GPtrArray
*props
);
82 static void line_set_props(Line
*line
, GPtrArray
*props
);
84 static void line_save(Line
*line
, ObjectNode obj_node
, const char *filename
);
85 static DiaObject
*line_load(ObjectNode obj_node
, int version
, const char *filename
);
86 static DiaMenu
*line_get_object_menu(Line
*line
, Point
*clickedpoint
);
88 void Line_adjust_for_absolute_gap(Line
*line
, Point
*gap_endpoints
);
90 static ObjectTypeOps line_type_ops
=
92 (CreateFunc
) line_create
,
95 (GetDefaultsFunc
) NULL
,
96 (ApplyDefaultsFunc
) NULL
99 DiaObjectType line_type
=
101 "Standard - Line", /* name */
103 (char **) line_icon
, /* pixmap */
104 &line_type_ops
/* ops */
107 DiaObjectType
*_line_type
= (DiaObjectType
*) &line_type
;
109 static ObjectOps line_ops
= {
110 (DestroyFunc
) line_destroy
,
111 (DrawFunc
) line_draw
,
112 (DistanceFunc
) line_distance_from
,
113 (SelectFunc
) line_select
,
114 (CopyFunc
) line_copy
,
115 (MoveFunc
) line_move
,
116 (MoveHandleFunc
) line_move_handle
,
117 (GetPropertiesFunc
) object_create_props_dialog
,
118 (ApplyPropertiesFunc
) object_apply_props_from_dialog
,
119 (ObjectMenuFunc
) line_get_object_menu
,
120 (DescribePropsFunc
) line_describe_props
,
121 (GetPropsFunc
) line_get_props
,
122 (SetPropsFunc
) line_set_props
125 static PropNumData gap_range
= { -G_MAXFLOAT
, G_MAXFLOAT
, 0.1};
127 static PropDescription line_props
[] = {
128 OBJECT_COMMON_PROPERTIES
,
130 PROP_STD_LINE_COLOUR
,
132 PROP_FRAME_BEGIN("arrows",PROP_FLAG_STANDARD
,N_("Arrows")),
133 PROP_STD_START_ARROW
,
135 PROP_FRAME_END("arrows",PROP_FLAG_STANDARD
),
136 { "start_point", PROP_TYPE_POINT
, 0,
137 N_("Start point"), NULL
},
138 { "end_point", PROP_TYPE_POINT
, 0,
139 N_("End point"), NULL
},
141 PROP_FRAME_BEGIN("gaps",0,N_("Line gaps")),
142 { "absolute_start_gap", PROP_TYPE_REAL
, PROP_FLAG_VISIBLE
,
143 N_("Absolute start gap"), NULL
, &gap_range
},
144 { "absolute_end_gap", PROP_TYPE_REAL
, PROP_FLAG_VISIBLE
,
145 N_("Absolute end gap"), NULL
, &gap_range
},
146 PROP_FRAME_END("gaps",0),
151 static PropDescription
*
152 line_describe_props(Line
*line
)
154 if (line_props
[0].quark
== 0)
155 prop_desc_list_calculate_quarks(line_props
);
159 static PropOffset line_offsets
[] = {
160 OBJECT_COMMON_PROPERTIES_OFFSETS
,
161 { "line_width", PROP_TYPE_REAL
, offsetof(Line
, line_width
) },
162 { "line_colour", PROP_TYPE_COLOUR
, offsetof(Line
, line_color
) },
163 { "line_style", PROP_TYPE_LINESTYLE
,
164 offsetof(Line
, line_style
), offsetof(Line
, dashlength
) },
165 { "start_arrow", PROP_TYPE_ARROW
, offsetof(Line
, start_arrow
) },
166 { "end_arrow", PROP_TYPE_ARROW
, offsetof(Line
, end_arrow
) },
167 { "start_point", PROP_TYPE_POINT
, offsetof(Connection
, endpoints
[0]) },
168 { "end_point", PROP_TYPE_POINT
, offsetof(Connection
, endpoints
[1]) },
169 { "absolute_start_gap", PROP_TYPE_REAL
, offsetof(Line
, absolute_start_gap
) },
170 { "absolute_end_gap", PROP_TYPE_REAL
, offsetof(Line
, absolute_end_gap
) },
175 line_get_props(Line
*line
, GPtrArray
*props
)
177 object_get_props_from_offsets(&line
->connection
.object
,
178 line_offsets
, props
);
182 line_set_props(Line
*line
, GPtrArray
*props
)
184 object_set_props_from_offsets(&line
->connection
.object
,
185 line_offsets
, props
);
186 line_update_data(line
);
190 line_init_defaults() {
191 static int defaults_initialized
= 0;
193 if (!defaults_initialized
) {
194 default_properties
.absolute_start_gap
= 0.0;
195 default_properties
.absolute_end_gap
= 0.0;
196 defaults_initialized
= 1;
200 static ObjectChange
*
201 line_add_connpoint_callback(DiaObject
*obj
, Point
*clicked
, gpointer data
)
204 oc
= connpointline_add_point(((Line
*)obj
)->cpl
,clicked
);
205 line_update_data((Line
*)obj
);
209 static ObjectChange
*
210 line_remove_connpoint_callback(DiaObject
*obj
, Point
*clicked
, gpointer data
)
213 oc
= connpointline_remove_point(((Line
*)obj
)->cpl
,clicked
);
214 line_update_data((Line
*)obj
);
218 static DiaMenuItem object_menu_items
[] = {
219 { N_("Add connection point"), line_add_connpoint_callback
, NULL
, 1 },
220 { N_("Delete connection point"), line_remove_connpoint_callback
,
224 static DiaMenu object_menu
= {
226 sizeof(object_menu_items
)/sizeof(DiaMenuItem
),
232 line_get_object_menu(Line
*line
, Point
*clickedpoint
)
237 /* Set entries sensitive/selected etc here */
238 object_menu_items
[0].active
=
239 connpointline_can_add_point(cpl
, clickedpoint
);
240 object_menu_items
[1].active
=
241 connpointline_can_remove_point(cpl
,clickedpoint
);
246 About start_gap, end_gap, auto/absolute
248 Place positive reals (try 1.0, for instance) in absolute
249 to create gaps on line end/start.
250 If auto_gap is false, these gaps are of that length.
251 If a gap is negative, the line extends past the handle.
252 If auto_gap is true, the gap length is computed so it touches
253 the connected object edge then the absolute gap is added
257 calculate_object_edge(Point
*objmid
, Point
*end
, DiaObject
*obj
)
261 Point trace
[MAXITER
];
262 real disttrace
[MAXITER
];
264 Point mid1
, mid2
, mid3
;
269 mid2
.x
= (objmid
->x
+end
->x
)/2;
270 mid2
.y
= (objmid
->y
+end
->y
)/2;
273 /* If the other end is inside the object */
274 dist
= obj
->ops
->distance_from(obj
, &mid3
);
275 if (dist
< 0.001) return mid1
;
279 dist
= obj
->ops
->distance_from(obj
, &mid2
);
280 if (dist
< 0.0000001) {
285 mid2
.x
= (mid1
.x
+ mid3
.x
)/2;
286 mid2
.y
= (mid1
.y
+ mid3
.y
)/2;
292 } while (i
< MAXITER
&& (dist
< 0.0000001 || dist
> 0.001));
296 for (i
= 0; i
< MAXITER
; i
++) {
297 printf("%d: %f, %f: %f\n", i
, trace
[i
].x
, trace
[i
].y
, disttrace
[i
]);
299 printf("i = %d, dist = %f\n", i
, dist
);
307 /** Calculate the absolute gap -- this gap is 'transient', in that
308 * the actual end of the line is not moved, but it is made to look like
312 line_adjust_for_absolute_gap(Line
*line
, Point
*gap_endpoints
)
317 endpoints
[0] = line
->connection
.endpoints
[0];
318 endpoints
[1] = line
->connection
.endpoints
[1];
320 line_length
= distance_point_point(&endpoints
[0], &endpoints
[1]);
322 /* puts new 0 to x% of 0->1 */
323 point_convex(&gap_endpoints
[0], &endpoints
[0], &endpoints
[1],
324 1 - line
->absolute_start_gap
/line_length
);
326 /* puts new 1 to x% of 1->0 */
327 point_convex(&gap_endpoints
[1], &endpoints
[1], &endpoints
[0],
328 1 - line
->absolute_end_gap
/line_length
);
332 line_distance_from(Line
*line
, Point
*point
)
336 endpoints
= &line
->connection
.endpoints
[0];
338 if (line
->absolute_start_gap
|| line
->absolute_end_gap
) {
339 Point gap_endpoints
[2]; /* Visible endpoints of line */
341 line_adjust_for_absolute_gap(line
, gap_endpoints
);
342 return distance_line_point( &gap_endpoints
[0], &gap_endpoints
[1],
343 line
->line_width
, point
);
345 return distance_line_point( &endpoints
[0], &endpoints
[1],
346 line
->line_width
, point
);
351 line_select(Line
*line
, Point
*clicked_point
,
352 DiaRenderer
*interactive_renderer
)
354 connection_update_handles(&line
->connection
);
358 line_move_handle(Line
*line
, Handle
*handle
,
359 Point
*to
, ConnectionPoint
*cp
,
360 HandleMoveReason reason
, ModifierKeys modifiers
)
363 assert(handle
!=NULL
);
366 connection_move_handle(&line
->connection
, handle
->id
, to
, cp
, reason
, modifiers
);
368 line_update_data(line
);
374 line_move(Line
*line
, Point
*to
)
377 Point
*endpoints
= &line
->connection
.endpoints
[0];
379 start_to_end
= endpoints
[1];
380 point_sub(&start_to_end
, &endpoints
[0]);
382 endpoints
[1] = endpoints
[0] = *to
;
383 point_add(&endpoints
[1], &start_to_end
);
385 line_update_data(line
);
391 line_draw(Line
*line
, DiaRenderer
*renderer
)
393 Point gap_endpoints
[2];
395 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
397 assert(line
!= NULL
);
398 assert(renderer
!= NULL
);
400 renderer_ops
->set_linewidth(renderer
, line
->line_width
);
401 renderer_ops
->set_linestyle(renderer
, line
->line_style
);
402 renderer_ops
->set_dashlength(renderer
, line
->dashlength
);
403 renderer_ops
->set_linecaps(renderer
, LINECAPS_BUTT
);
405 if (line
->absolute_start_gap
|| line
->absolute_end_gap
) {
406 line_adjust_for_absolute_gap(line
, gap_endpoints
);
408 renderer_ops
->draw_line_with_arrows(renderer
,
409 &gap_endpoints
[0], &gap_endpoints
[1],
415 renderer_ops
->draw_line_with_arrows(renderer
,
416 &line
->connection
.endpoints
[0],
417 &line
->connection
.endpoints
[1],
427 line_create(Point
*startpoint
,
435 Point defaultlen
= { 1.0, 1.0 };
437 line_init_defaults();
439 line
= g_malloc0(sizeof(Line
));
441 line
->line_width
= attributes_get_default_linewidth();
442 line
->line_color
= attributes_get_foreground();
443 line
->absolute_start_gap
= default_properties
.absolute_start_gap
;
444 line
->absolute_end_gap
= default_properties
.absolute_end_gap
;
446 conn
= &line
->connection
;
447 conn
->endpoints
[0] = *startpoint
;
448 conn
->endpoints
[1] = *startpoint
;
449 point_add(&conn
->endpoints
[1], &defaultlen
);
453 obj
->type
= &line_type
;
454 obj
->ops
= &line_ops
;
456 connection_init(conn
, 2, 0);
458 line
->cpl
= connpointline_create(obj
,1);
460 attributes_get_default_line_style(&line
->line_style
, &line
->dashlength
);
461 line
->start_arrow
= attributes_get_default_start_arrow();
462 line
->end_arrow
= attributes_get_default_end_arrow();
463 line_update_data(line
);
465 *handle1
= obj
->handles
[0];
466 *handle2
= obj
->handles
[1];
467 return &line
->connection
.object
;
471 line_destroy(Line
*line
)
473 connection_destroy(&line
->connection
);
477 line_copy(Line
*line
)
480 Connection
*conn
, *newconn
;
484 conn
= &line
->connection
;
486 newline
= g_malloc0(sizeof(Line
));
487 newconn
= &newline
->connection
;
488 newobj
= &newconn
->object
;
490 connection_copy(conn
, newconn
);
492 newline
->cpl
= connpointline_copy(newobj
,line
->cpl
,&rcc
);
494 newline
->line_color
= line
->line_color
;
495 newline
->line_width
= line
->line_width
;
496 newline
->line_style
= line
->line_style
;
497 newline
->dashlength
= line
->dashlength
;
498 newline
->start_arrow
= line
->start_arrow
;
499 newline
->end_arrow
= line
->end_arrow
;
500 newline
->absolute_start_gap
= line
->absolute_start_gap
;
501 newline
->absolute_end_gap
= line
->absolute_end_gap
;
503 line_update_data(line
);
505 return &newline
->connection
.object
;
509 line_update_data(Line
*line
)
511 Connection
*conn
= &line
->connection
;
512 DiaObject
*obj
= &conn
->object
;
513 LineBBExtras
*extra
= &conn
->extra_spacing
;
516 extra
->start_trans
= (line
->line_width
/ 2.0);
517 extra
->end_trans
= (line
->line_width
/ 2.0);
518 extra
->start_long
= (line
->line_width
/ 2.0);
519 extra
->end_long
= (line
->line_width
/ 2.0);
520 if (line
->start_arrow
.type
!= ARROW_NONE
)
521 extra
->start_trans
= MAX(extra
->start_trans
,line
->start_arrow
.width
);
522 if (line
->end_arrow
.type
!= ARROW_NONE
)
523 extra
->end_trans
= MAX(extra
->end_trans
,line
->end_arrow
.width
);
525 if (connpoint_is_autogap(line
->connection
.endpoint_handles
[0].connected_to
) ||
526 connpoint_is_autogap(line
->connection
.endpoint_handles
[1].connected_to
)) {
527 connection_adjust_for_autogap(line
);
529 if (line
->absolute_start_gap
|| line
->absolute_end_gap
) {
530 Point gap_endpoints
[2];
532 line_adjust_for_absolute_gap(line
, gap_endpoints
);
533 line_bbox(&gap_endpoints
[0],&gap_endpoints
[1],
534 &conn
->extra_spacing
,&conn
->object
.bounding_box
);
535 start
= gap_endpoints
[0];
536 end
= gap_endpoints
[1];
538 connection_update_boundingbox(conn
);
539 start
= conn
->endpoints
[0];
540 end
= conn
->endpoints
[1];
543 obj
->position
= conn
->endpoints
[0];
545 connpointline_update(line
->cpl
);
546 connpointline_putonaline(line
->cpl
,&start
, &end
);
548 connection_update_handles(conn
);
553 line_save(Line
*line
, ObjectNode obj_node
, const char *filename
)
555 dia_object_sanity_check((DiaObject
*)line
, "Saving line");
557 connection_save(&line
->connection
, obj_node
);
559 connpointline_save(line
->cpl
,obj_node
,"numcp");
561 if (!color_equals(&line
->line_color
, &color_black
))
562 data_add_color(new_attribute(obj_node
, "line_color"),
565 if (line
->line_width
!= 0.1)
566 data_add_real(new_attribute(obj_node
, "line_width"),
569 if (line
->line_style
!= LINESTYLE_SOLID
)
570 data_add_enum(new_attribute(obj_node
, "line_style"),
573 if (line
->start_arrow
.type
!= ARROW_NONE
) {
574 save_arrow(obj_node
, &line
->start_arrow
,
575 "start_arrow", "start_arrow_length", "startend_arrow_width");
578 if (line
->end_arrow
.type
!= ARROW_NONE
) {
579 save_arrow(obj_node
, &line
->end_arrow
,
580 "end_arrow", "end_arrow_length", "end_arrow_width");
583 if (line
->absolute_start_gap
)
584 data_add_real(new_attribute(obj_node
, "absolute_start_gap"),
585 line
->absolute_start_gap
);
586 if (line
->absolute_end_gap
)
587 data_add_real(new_attribute(obj_node
, "absolute_end_gap"),
588 line
->absolute_end_gap
);
590 if (line
->line_style
!= LINESTYLE_SOLID
&& line
->dashlength
!= DEFAULT_LINESTYLE_DASHLEN
)
591 data_add_real(new_attribute(obj_node
, "dashlength"),
596 line_load(ObjectNode obj_node
, int version
, const char *filename
)
603 line
= g_malloc0(sizeof(Line
));
605 conn
= &line
->connection
;
608 obj
->type
= &line_type
;
609 obj
->ops
= &line_ops
;
611 connection_load(conn
, obj_node
);
613 line
->line_color
= color_black
;
614 attr
= object_find_attribute(obj_node
, "line_color");
616 data_color(attribute_first_data(attr
), &line
->line_color
);
618 line
->line_width
= 0.1;
619 attr
= object_find_attribute(obj_node
, "line_width");
621 line
->line_width
= data_real(attribute_first_data(attr
));
623 line
->line_style
= LINESTYLE_SOLID
;
624 attr
= object_find_attribute(obj_node
, "line_style");
626 line
->line_style
= data_enum(attribute_first_data(attr
));
628 load_arrow(obj_node
, &line
->start_arrow
,
629 "start_arrow", "start_arrow_length", "start_arrow_width");
631 load_arrow(obj_node
, &line
->end_arrow
,
632 "end_arrow", "end_arrow_length", "end_arrow_width");
634 line
->absolute_start_gap
= 0.0;
635 attr
= object_find_attribute(obj_node
, "absolute_start_gap");
637 line
->absolute_start_gap
= data_real( attribute_first_data(attr
) );
638 line
->absolute_end_gap
= 0.0;
639 attr
= object_find_attribute(obj_node
, "absolute_end_gap");
641 line
->absolute_end_gap
= data_real( attribute_first_data(attr
) );
643 line
->dashlength
= DEFAULT_LINESTYLE_DASHLEN
;
644 attr
= object_find_attribute(obj_node
, "dashlength");
646 line
->dashlength
= data_real(attribute_first_data(attr
));
648 connection_init(conn
, 2, 0);
650 line
->cpl
= connpointline_load(obj
,obj_node
,"numcp",1,NULL
);
651 line_update_data(line
);
653 return &line
->connection
.object
;