2006-12-05 David Lodge <dave@cirt.net>
[dia.git] / lib / orth_conn.c
blobc86997a9c5d8e6b6f5d637cedc87de57bec09bf2
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 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.
18 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <assert.h>
23 #include <math.h>
24 #include <string.h> /* memcpy() */
25 #include <glib.h>
27 #include "orth_conn.h"
28 #include "message.h"
29 #include "diamenu.h"
30 #include "handle.h"
31 #include "diarenderer.h"
32 #include "autoroute.h"
34 static void place_handle_by_swapping(OrthConn *orth,
35 int index, Handle *handle);
36 static ObjectChange *orthconn_set_autorouting(OrthConn *orth, gboolean on);
38 enum change_type {
39 TYPE_ADD_SEGMENT,
40 TYPE_REMOVE_SEGMENT
43 static ObjectChange *
44 midsegment_create_change(OrthConn *orth, enum change_type type,
45 int segment,
46 Point *point1, Point *point2,
47 Handle *handle1, Handle *handle2);
49 struct MidSegmentChange {
50 ObjectChange obj_change;
52 /* All additions and deletions of segments in the middle
53 * of the orthconn must delete/add two segments to keep
54 * the horizontal/vertical balance.
56 * None of the end segments must be removed by this change.
59 enum change_type type;
60 int applied;
62 int segment;
63 Point points[2];
64 Handle *handles[2]; /* These handles cannot be connected */
67 static ObjectChange *
68 endsegment_create_change(OrthConn *orth, enum change_type type,
69 int segment, Point *point,
70 Handle *handle);
71 static void
72 place_handle_by_swapping(OrthConn *orth, int index, Handle *handle);
74 struct EndSegmentChange {
75 ObjectChange obj_change;
77 /* Additions and deletions of segments of at the endpoints
78 * of the orthconn.
80 * Addition of an endpoint segment need not store any point.
81 * Deletion of an endpoint segment need to store the endpoint position
82 * so that it can be reverted.
83 * Deleted segments might be connected, so we must store the connection
84 * point.
87 enum change_type type;
88 int applied;
90 int segment;
91 Point point;
92 Handle *handle;
93 Handle *old_end_handle;
94 ConnectionPoint *cp; /* NULL in add segment and if not connected in
95 remove segment */
98 static ObjectChange*
99 autoroute_create_change(OrthConn *orth, gboolean on);
101 struct AutorouteChange {
102 ObjectChange obj_change;
103 gboolean on;
104 Point *points;
107 static void set_midpoint(Point *point, OrthConn *orth, int segment)
109 int i = segment;
110 point->x = 0.5*(orth->points[i].x + orth->points[i+1].x);
111 point->y = 0.5*(orth->points[i].y + orth->points[i+1].y);
114 static void setup_midpoint_handle(Handle *handle)
116 handle->id = HANDLE_MIDPOINT;
117 handle->type = HANDLE_MINOR_CONTROL;
118 handle->connect_type = HANDLE_NONCONNECTABLE;
119 handle->connected_to = NULL;
122 static void setup_endpoint_handle(Handle *handle, HandleId id )
124 handle->id = id;
125 handle->type = HANDLE_MAJOR_CONTROL;
126 handle->connect_type = HANDLE_CONNECTABLE;
127 handle->connected_to = NULL;
130 static int get_handle_nr(OrthConn *orth, Handle *handle)
132 int i = 0;
133 for (i=0;i<orth->numpoints-1;i++) {
134 if (orth->handles[i] == handle)
135 return i;
137 return -1;
140 static int get_segment_nr(OrthConn *orth, Point *point, real max_dist)
142 int i;
143 int segment;
144 real distance, tmp_dist;
146 segment = 0;
147 distance = distance_line_point(&orth->points[0], &orth->points[1], 0, point);
149 for (i=1;i<orth->numpoints-1;i++) {
150 tmp_dist = distance_line_point(&orth->points[i], &orth->points[i+1], 0, point);
151 if (tmp_dist < distance) {
152 segment = i;
153 distance = tmp_dist;
157 if (distance < max_dist)
158 return segment;
160 return -1;
164 ObjectChange *
165 orthconn_move_handle(OrthConn *orth, Handle *handle,
166 Point *to, ConnectionPoint *cp,
167 HandleMoveReason reason, ModifierKeys modifiers)
169 int n;
170 int handle_nr;
171 DiaObject *obj = (DiaObject *)orth;
172 ObjectChange *change = NULL;
174 switch(handle->id) {
175 case HANDLE_MOVE_STARTPOINT:
176 orth->points[0] = *to;
177 if (orth->autorouting &&
178 autoroute_layout_orthconn(orth, cp,
179 obj->handles[1]->connected_to))
180 break;
181 switch (orth->orientation[0]) {
182 case HORIZONTAL:
183 orth->points[1].y = to->y;
184 break;
185 case VERTICAL:
186 orth->points[1].x = to->x;
187 break;
189 break;
190 case HANDLE_MOVE_ENDPOINT:
191 n = orth->numpoints - 1;
192 orth->points[n] = *to;
193 if (orth->autorouting &&
194 autoroute_layout_orthconn(orth, obj->handles[0]->connected_to,
195 cp))
196 break;
197 switch (orth->orientation[n-1]) {
198 case HORIZONTAL:
199 orth->points[n-1].y = to->y;
200 break;
201 case VERTICAL:
202 orth->points[n-1].x = to->x;
203 break;
205 break;
206 case HANDLE_MIDPOINT:
207 n = orth->numpoints - 1;
208 handle_nr = get_handle_nr(orth, handle);
209 if (orth->autorouting)
210 change = orthconn_set_autorouting(orth, FALSE);
211 switch (orth->orientation[handle_nr]) {
212 case HORIZONTAL:
213 orth->points[handle_nr].y = to->y;
214 orth->points[handle_nr+1].y = to->y;
215 break;
216 case VERTICAL:
217 orth->points[handle_nr].x = to->x;
218 orth->points[handle_nr+1].x = to->x;
219 break;
221 break;
222 default:
223 message_error("Internal error in orthconn_move_handle.\n");
224 break;
227 return change;
230 ObjectChange *
231 orthconn_move(OrthConn *orth, Point *to)
233 Point p;
234 int i;
236 p = *to;
237 point_sub(&p, &orth->points[0]);
239 orth->points[0] = *to;
240 for (i=1;i<orth->numpoints;i++) {
241 point_add(&orth->points[i], &p);
243 return NULL;
246 real
247 orthconn_distance_from(OrthConn *orth, Point *point, real line_width)
249 int i;
250 real dist;
252 dist = distance_line_point( &orth->points[0], &orth->points[1],
253 line_width, point);
254 for (i=1;i<orth->numpoints-1;i++) {
255 dist = MIN(dist,
256 distance_line_point( &orth->points[i], &orth->points[i+1],
257 line_width, point));
259 return dist;
263 static void
264 adjust_handle_count_to(OrthConn *orth, guint count) {
265 /* This will shrink or expand orth->handles as necessary (so that
266 orth->numhandles matches orth->numpoints-1, most probably), by adding or
267 removing minor handles and keeping the endpoint handles at the
268 extremities of the array. */
270 if (orth->numhandles == count) return;
271 if (orth->numhandles < count) { /* adding */
272 int i;
273 orth->handles = g_realloc(orth->handles,
274 (count)*sizeof(Handle *));
275 orth->handles[count-1] = orth->handles[orth->numhandles-1];
276 orth->handles[orth->numhandles-1] = NULL;
277 for (i=orth->numhandles-1; i<count-1; i++) {
278 Handle *handle = g_new0(Handle,1);
279 setup_midpoint_handle(handle);
280 object_add_handle(&orth->object,handle);
281 orth->handles[i] = handle;
283 } else { /* removing */
284 int i;
285 for (i=count-1; i<orth->numhandles-1; i++) {
286 Handle *handle = orth->handles[i];
287 object_remove_handle(&orth->object,handle);
288 g_free(handle);
289 orth->handles[i] = NULL;
291 orth->handles[count-1] = orth->handles[orth->numhandles-1];
292 orth->handles[orth->numhandles-1] = NULL;
293 orth->handles = g_realloc(orth->handles,
294 (count)*sizeof(Handle *));
296 orth->numhandles = count;
297 /* handles' positions will be set now */
300 void
301 orthconn_update_data(OrthConn *orth)
303 int i;
304 DiaObject *obj = (DiaObject *)orth;
305 Point *points;
306 ConnectionPoint *start_cp;
307 ConnectionPoint *end_cp;
309 obj->position = orth->points[0];
311 /* During startup, handles may not have been setup yet, so do so
312 * temporarily to be able to get the last handle connection.
314 adjust_handle_count_to(orth, orth->numpoints-1);
316 start_cp = orth->handles[0]->connected_to;
317 end_cp = orth->handles[orth->numpoints-2]->connected_to;
319 if (!orth->points) {
320 g_warning("very sick OrthConn object...");
321 return;
324 points = orth->points;
325 if (!orth->autorouting &&
326 (connpoint_is_autogap(start_cp) ||
327 connpoint_is_autogap(end_cp))) {
328 Point* new_points = g_new(Point, orth->numpoints);
329 int i;
330 for (i = 0; i < orth->numpoints; i++) {
331 new_points[i] = points[i];
334 if (connpoint_is_autogap(start_cp)) {
335 new_points[0] = calculate_object_edge(&start_cp->pos, &new_points[1],
336 start_cp->object);
338 printf("Moved start to %f, %f\n",
339 new_points[0].x, new_points[0].y);
342 if (connpoint_is_autogap(end_cp)) {
343 new_points[orth->numpoints-1] =
344 calculate_object_edge(&end_cp->pos, &new_points[orth->numpoints-2],
345 end_cp->object);
347 printf("Moved end to %f, %f\n",
348 new_points[orth->numpoints-1].x, new_points[orth->numpoints-1].y);
351 g_free(points);
352 orth->points = new_points;
355 obj->position = orth->points[0];
357 adjust_handle_count_to(orth, orth->numpoints-1);
359 /* Make sure start-handle is first and end-handle is second. */
360 place_handle_by_swapping(orth, 0, orth->handles[0]);
361 place_handle_by_swapping(orth, 1, orth->handles[orth->numpoints-2]);
363 /* Update handles: */
364 orth->handles[0]->pos = orth->points[0];
365 orth->handles[orth->numpoints-2]->pos = orth->points[orth->numpoints-1];
367 for (i=1;i<orth->numpoints-2;i++) {
368 set_midpoint(&orth->handles[i]->pos, orth, i);
372 void
373 orthconn_update_boundingbox(OrthConn *orth)
375 assert(orth != NULL);
376 polyline_bbox(&orth->points[0],
377 orth->numpoints,
378 &orth->extra_spacing, FALSE,
379 &orth->object.bounding_box);
382 void
383 orthconn_simple_draw(OrthConn *orth, DiaRenderer *renderer, real width)
385 Point *points;
387 assert(orth != NULL);
388 assert(renderer != NULL);
390 if (!orth->points) {
391 g_warning("very sick OrthConn object...");
392 return;
395 /* When not autorouting, need to take gap into account here. */
396 points = &orth->points[0];
398 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
399 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
400 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_MITER);
401 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
403 DIA_RENDERER_GET_CLASS(renderer)->draw_polyline(renderer, points,
404 orth->numpoints,
405 &color_black);
410 orthconn_can_delete_segment(OrthConn *orth, Point *clickedpoint)
412 int segment;
414 /* Cannot delete any segments when there are only two left,
415 * and not amy middle segment if there are only three segments.
418 if (orth->numpoints==3)
419 return 0;
421 segment = get_segment_nr(orth, clickedpoint, 1.0);
423 if (segment<0)
424 return 0;
426 if ( (segment != 0) && (segment != orth->numpoints-2)) {
427 /* middle segment */
428 if (orth->numpoints==4)
429 return 0;
432 return 1;
436 orthconn_can_add_segment(OrthConn *orth, Point *clickedpoint)
438 int segment = get_segment_nr(orth, clickedpoint, 1000000.0);
440 if (segment<0)
441 return 0;
443 return 1;
447 /* Needs to have at least 2 handles.
448 The handles are stored in order in the OrthConn, but need
449 not be stored in order in the DiaObject.handles array. This
450 is so that derived object can do what they want with
451 DiaObject.handles. */
453 void
454 orthconn_init(OrthConn *orth, Point *startpoint)
456 DiaObject *obj;
458 obj = &orth->object;
460 object_init(obj, 3, 0);
462 orth->numpoints = 4;
463 orth->numorient = orth->numpoints - 1;
465 orth->points = g_malloc0(4*sizeof(Point));
467 orth->orientation = g_malloc0(3*sizeof(Orientation));
469 orth->numhandles = 3;
470 orth->handles = g_malloc0(3*sizeof(Handle *));
472 orth->handles[0] = g_new(Handle, 1);
473 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
474 obj->handles[0] = orth->handles[0];
476 orth->handles[1] = g_new(Handle, 1);
477 setup_midpoint_handle(orth->handles[1]);
478 obj->handles[1] = orth->handles[1];
480 orth->handles[2] = g_new(Handle, 1);
481 setup_endpoint_handle(orth->handles[2], HANDLE_MOVE_ENDPOINT);
482 obj->handles[2] = orth->handles[2];
484 orth->autorouting = TRUE;
486 /* Just so we have some position: */
487 orth->points[0] = *startpoint;
488 orth->points[1].x = startpoint->x;
489 orth->points[1].y = startpoint->y + 1.0;
490 orth->points[2].x = startpoint->x + 1.0;
491 orth->points[2].y = startpoint->y + 1.0;
492 orth->points[3].x = startpoint->x + 2.0;
493 orth->points[3].y = startpoint->y + 1.0;
495 orth->orientation[0] = VERTICAL;
496 orth->orientation[1] = HORIZONTAL;
497 orth->orientation[2] = VERTICAL;
499 orthconn_update_data(orth);
502 /** This function does *not* set up handles */
503 void
504 orthconn_set_points(OrthConn *orth, int num_points, Point *points)
506 int i;
507 gboolean horiz;
509 orth->numpoints = num_points;
511 if (orth->points)
512 g_free(orth->points);
514 orth->points = g_malloc((orth->numpoints)*sizeof(Point));
516 for (i=0;i<orth->numpoints;i++) {
517 orth->points[i] = points[i];
520 /* Set up the orientation array. */
521 /* Maybe we could get rid of this array altogether? */
522 orth->numorient = orth->numpoints-1;
523 if (orth->orientation) g_free(orth->orientation);
524 orth->orientation = g_new(Orientation, orth->numorient);
525 horiz = (fabs(orth->points[0].y-orth->points[1].y) < 0.00001);
526 for (i = 0; i < orth->numorient; i++) {
527 if (horiz) orth->orientation[i] = HORIZONTAL;
528 else orth->orientation[i] = VERTICAL;
529 horiz = !horiz;
533 void
534 orthconn_copy(OrthConn *from, OrthConn *to)
536 int i;
537 DiaObject *toobj, *fromobj;
539 toobj = &to->object;
540 fromobj = &from->object;
542 object_copy(fromobj, toobj);
544 to->numpoints = from->numpoints;
545 to->numorient = from->numorient;
547 to->points = g_malloc0((to->numpoints)*sizeof(Point));
549 for (i=0;i<to->numpoints;i++) {
550 to->points[i] = from->points[i];
553 to->autorouting = from->autorouting;
554 to->orientation = g_malloc0((to->numpoints-1)*sizeof(Orientation));
555 to->numhandles = from->numhandles;
556 to->handles = g_malloc0((to->numpoints-1)*sizeof(Handle *));
558 for (i=0;i<to->numpoints-1;i++) {
559 to->orientation[i] = from->orientation[i];
560 to->handles[i] = g_new(Handle,1);
561 *to->handles[i] = *from->handles[i];
562 to->handles[i]->connected_to = NULL;
563 toobj->handles[i] = to->handles[i];
565 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
568 void
569 orthconn_destroy(OrthConn *orth)
571 int i;
573 object_destroy(&orth->object);
575 g_free(orth->points);
576 g_free(orth->orientation);
578 for (i=0;i<orth->numpoints-1;i++)
579 g_free(orth->handles[i]);
581 g_free(orth->handles);
584 static void
585 place_handle_by_swapping(OrthConn *orth, int index, Handle *handle)
587 DiaObject *obj;
588 Handle *tmp;
589 int j;
591 obj = (DiaObject *)orth;
592 if (obj->handles[index] == handle)
593 return; /* Nothing to do */
595 for (j=0;j<obj->num_handles;j++) {
596 if (obj->handles[j] == handle) {
597 /* Swap handle j and index */
598 tmp = obj->handles[j];
599 obj->handles[j] = obj->handles[index];
600 obj->handles[index] = tmp;
602 return;
607 void
608 orthconn_save(OrthConn *orth, ObjectNode obj_node)
610 int i;
611 AttributeNode attr;
613 /* Make sure start-handle is first and end-handle is second. */
614 place_handle_by_swapping(orth, 0, orth->handles[0]);
615 place_handle_by_swapping(orth, 1, orth->handles[orth->numpoints-2]);
617 object_save(&orth->object, obj_node);
619 attr = new_attribute(obj_node, "orth_points");
621 for (i=0;i<orth->numpoints;i++) {
622 data_add_point(attr, &orth->points[i]);
625 attr = new_attribute(obj_node, "orth_orient");
626 for (i=0;i<orth->numpoints-1;i++) {
627 data_add_enum(attr, orth->orientation[i]);
630 data_add_boolean(new_attribute(obj_node, "autorouting"), orth->autorouting);
633 void
634 orthconn_load(OrthConn *orth, ObjectNode obj_node) /* NOTE: Does object_init() */
636 int i;
637 AttributeNode attr;
638 DataNode data;
639 int n;
640 int version = 0;
642 DiaObject *obj = &orth->object;
644 object_load(obj, obj_node);
646 attr = object_find_attribute(obj_node, "version");
647 if (attr != NULL)
648 version = attribute_num_data(attr);
650 attr = object_find_attribute(obj_node, "orth_points");
652 if (attr != NULL)
653 orth->numpoints = attribute_num_data(attr);
654 else
655 orth->numpoints = 0;
657 orth->numorient = orth->numpoints - 1;
659 object_init(obj, orth->numpoints-1, 0);
661 data = attribute_first_data(attr);
662 orth->points = g_malloc0((orth->numpoints)*sizeof(Point));
663 for (i=0;i<orth->numpoints;i++) {
664 data_point(data, &orth->points[i]);
665 data = data_next(data);
668 attr = object_find_attribute(obj_node, "orth_orient");
670 data = attribute_first_data(attr);
671 orth->orientation = g_malloc0((orth->numpoints-1)*sizeof(Orientation));
672 for (i=0;i<orth->numpoints-1;i++) {
673 orth->orientation[i] = data_enum(data);
674 data = data_next(data);
677 orth->autorouting = TRUE;
678 attr = object_find_attribute(obj_node, "autorouting");
679 if (attr != NULL)
680 orth->autorouting = data_boolean(attribute_first_data(attr));
681 else if (version == 0) {
682 /* Version 0 orthconns have no autorouting. */
683 orth->autorouting = FALSE;
686 orth->handles = g_malloc0((orth->numpoints-1)*sizeof(Handle *));
688 orth->handles[0] = g_new(Handle, 1);
689 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
690 orth->handles[0]->pos = orth->points[0];
691 obj->handles[0] = orth->handles[0];
693 n = orth->numpoints-2;
694 orth->handles[n] = g_new(Handle, 1);
695 setup_endpoint_handle(orth->handles[n], HANDLE_MOVE_ENDPOINT);
696 orth->handles[n]->pos = orth->points[orth->numpoints-1];
697 obj->handles[1] = orth->handles[n];
699 for (i=1; i<orth->numpoints-2; i++) {
700 orth->handles[i] = g_new(Handle, 1);
701 setup_midpoint_handle(orth->handles[i]);
702 obj->handles[i+1] = orth->handles[i];
704 orth->numhandles = orth->numpoints-1;
706 orthconn_update_data(orth);
709 Handle*
710 orthconn_get_middle_handle( OrthConn *orth )
712 int n = orth->numpoints - 1 ;
713 return orth->handles[ n/2 ] ;
716 ObjectChange *
717 orthconn_delete_segment(OrthConn *orth, Point *clickedpoint)
719 int segment;
720 ObjectChange *change = NULL;
722 if (orth->numpoints==3)
723 return NULL;
725 segment = get_segment_nr(orth, clickedpoint, 1.0);
726 if (segment < 0)
727 return NULL;
729 if (segment==0) {
730 change = endsegment_create_change(orth, TYPE_REMOVE_SEGMENT, segment,
731 &orth->points[segment],
732 orth->handles[segment]);
733 } else if (segment == orth->numpoints-2) {
734 change = endsegment_create_change(orth, TYPE_REMOVE_SEGMENT, segment,
735 &orth->points[segment+1],
736 orth->handles[segment]);
737 } else if (segment > 0) {
738 /* Don't delete the last midpoint segment.
739 * That would delete also the endpoint segment after it.
741 if (segment == orth->numpoints-3)
742 segment--;
744 change = midsegment_create_change(orth, TYPE_REMOVE_SEGMENT, segment,
745 &orth->points[segment],
746 &orth->points[segment+1],
747 orth->handles[segment],
748 orth->handles[segment+1]);
751 change->apply(change, (DiaObject *)orth);
753 return change;
756 ObjectChange *
757 orthconn_add_segment(OrthConn *orth, Point *clickedpoint)
759 Handle *handle1, *handle2;
760 ObjectChange *change = NULL;
761 int segment;
762 Point newpoint;
764 segment = get_segment_nr(orth, clickedpoint, 1.0);
765 if (segment < 0)
766 return NULL;
768 if (segment==0) { /* First segment */
769 handle1 = g_new(Handle, 1);
770 setup_endpoint_handle(handle1, HANDLE_MOVE_STARTPOINT);
771 change = endsegment_create_change(orth, TYPE_ADD_SEGMENT,
772 0, &orth->points[0],
773 handle1);
774 } else if (segment == orth->numpoints-2) { /* Last segment */
775 handle1 = g_new(Handle, 1);
776 setup_endpoint_handle(handle1, HANDLE_MOVE_ENDPOINT);
777 change = endsegment_create_change(orth, TYPE_ADD_SEGMENT,
778 segment+1, &orth->points[segment+1],
779 handle1);
780 } else if (segment > 0) {
781 handle1 = g_new(Handle, 1);
782 setup_midpoint_handle(handle1);
783 handle2 = g_new(Handle, 1);
784 setup_midpoint_handle(handle2);
785 newpoint = *clickedpoint;
786 if (orth->orientation[segment]==HORIZONTAL)
787 newpoint.y = orth->points[segment].y;
788 else
789 newpoint.x = orth->points[segment].x;
791 change = midsegment_create_change(orth, TYPE_ADD_SEGMENT, segment,
792 &newpoint,
793 &newpoint,
794 handle1,
795 handle2);
798 change->apply(change, (DiaObject *)orth);
800 return change;
804 /* Set autorouting on or off. If setting on, try to autoroute and
805 * return the changes from that.
807 static ObjectChange *
808 orthconn_set_autorouting(OrthConn *conn, gboolean on)
810 DiaObject *obj = (DiaObject *)conn;
811 ObjectChange *change;
813 change = autoroute_create_change(conn, on);
814 change->apply(change, obj);
815 return change;
818 static void
819 delete_point(OrthConn *orth, int pos)
821 int i;
823 orth->numpoints--;
824 orth->numorient = orth->numpoints - 1;
826 for (i=pos;i<orth->numpoints;i++) {
827 orth->points[i] = orth->points[i+1];
830 orth->points = g_realloc(orth->points, orth->numpoints*sizeof(Point));
833 /* Make sure numpoints have been decreased before calling this function.
834 * ie. call delete_point first.
836 static void
837 remove_handle(OrthConn *orth, int segment)
839 int i;
840 Handle *handle;
842 handle = orth->handles[segment];
844 for (i=segment; i < orth->numpoints-1; i++) {
845 orth->handles[i] = orth->handles[i+1];
846 orth->orientation[i] = orth->orientation[i+1];
849 orth->orientation = g_realloc(orth->orientation,
850 (orth->numpoints-1)*sizeof(Orientation));
851 orth->handles = g_realloc(orth->handles,
852 (orth->numpoints-1)*sizeof(Handle *));
854 object_remove_handle(&orth->object, handle);
855 orth->numhandles = orth->numpoints-1;
859 static void
860 add_point(OrthConn *orth, int pos, Point *point)
862 int i;
864 orth->numpoints++;
865 orth->numorient = orth->numpoints-1;
867 orth->points = g_realloc(orth->points, orth->numpoints*sizeof(Point));
868 for (i=orth->numpoints-1;i>pos;i--) {
869 orth->points[i] = orth->points[i-1];
871 orth->points[pos] = *point;
874 /* Make sure numpoints have been increased before calling this function.
875 * ie. call add_point first.
877 static void
878 insert_handle(OrthConn *orth, int segment,
879 Handle *handle, Orientation orient)
881 int i;
883 orth->orientation = g_realloc(orth->orientation,
884 (orth->numpoints-1)*sizeof(Orientation));
885 orth->handles = g_realloc(orth->handles,
886 (orth->numpoints-1)*sizeof(Handle *));
887 for (i=orth->numpoints-2;i>segment;i--) {
888 orth->handles[i] = orth->handles[i-1];
889 orth->orientation[i] = orth->orientation[i-1];
891 orth->handles[segment] = handle;
892 orth->orientation[segment] = orient;
894 object_add_handle(&orth->object, handle);
895 orth->numhandles = orth->numpoints-1;
898 static void
899 endsegment_change_free(struct EndSegmentChange *change)
901 if ( (change->type==TYPE_ADD_SEGMENT && !change->applied) ||
902 (change->type==TYPE_REMOVE_SEGMENT && change->applied) ){
903 if (change->handle)
904 g_free(change->handle);
905 change->handle = NULL;
909 static void
910 endsegment_change_apply(struct EndSegmentChange *change, DiaObject *obj)
912 OrthConn *orth = (OrthConn *)obj;
914 change->applied = 1;
916 switch (change->type) {
917 case TYPE_ADD_SEGMENT:
918 object_unconnect(obj, change->old_end_handle);
919 if (change->segment==0) { /* first */
920 add_point(orth, 0, &change->point);
921 insert_handle(orth, change->segment,
922 change->handle, FLIP_ORIENT(orth->orientation[0]) );
923 setup_midpoint_handle(orth->handles[1]);
924 obj->position = orth->points[0];
925 } else { /* last */
926 add_point(orth, orth->numpoints, &change->point);
927 insert_handle(orth, change->segment, change->handle,
928 FLIP_ORIENT(orth->orientation[orth->numpoints-3]) );
929 setup_midpoint_handle(orth->handles[orth->numpoints-3]);
931 if (change->cp)
932 object_connect(obj, change->handle, change->cp);
933 break;
934 case TYPE_REMOVE_SEGMENT:
935 object_unconnect(obj, change->old_end_handle);
936 if (change->segment==0) { /* first */
937 delete_point(orth, 0);
938 remove_handle(orth, 0);
939 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
940 obj->position = orth->points[0];
941 } else { /* last */
942 delete_point(orth, orth->numpoints-1);
943 remove_handle(orth, change->segment);
944 setup_endpoint_handle(orth->handles[orth->numpoints-2],
945 HANDLE_MOVE_ENDPOINT);
947 break;
951 static void
952 endsegment_change_revert(struct EndSegmentChange *change, DiaObject *obj)
954 OrthConn *orth = (OrthConn *)obj;
956 switch (change->type) {
957 case TYPE_ADD_SEGMENT:
958 object_unconnect(obj, change->handle);
959 if (change->segment==0) { /* first */
960 delete_point(orth, 0);
961 remove_handle(orth, 0);
962 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
963 obj->position = orth->points[0];
964 } else { /* last */
965 delete_point(orth, orth->numpoints-1);
966 remove_handle(orth, change->segment);
967 setup_endpoint_handle(orth->handles[orth->numpoints-2],
968 HANDLE_MOVE_ENDPOINT);
970 if (change->cp)
971 object_connect(obj, change->old_end_handle, change->cp);
972 break;
973 case TYPE_REMOVE_SEGMENT:
974 if (change->segment==0) { /* first */
975 add_point(orth, 0, &change->point);
976 insert_handle(orth, change->segment,
977 change->handle, FLIP_ORIENT(orth->orientation[0]) );
978 setup_midpoint_handle(orth->handles[1]);
979 obj->position = orth->points[0];
980 } else { /* last */
981 add_point(orth, orth->numpoints, &change->point);
982 insert_handle(orth, change->segment, change->handle,
983 FLIP_ORIENT(orth->orientation[orth->numpoints-3]) );
984 setup_midpoint_handle(orth->handles[orth->numpoints-3]);
986 if (change->cp)
987 object_connect(obj, change->old_end_handle, change->cp);
988 break;
990 change->applied = 0;
993 static ObjectChange *
994 endsegment_create_change(OrthConn *orth, enum change_type type,
995 int segment, Point *point,
996 Handle *handle)
998 struct EndSegmentChange *change;
1000 change = g_new(struct EndSegmentChange, 1);
1002 change->obj_change.apply = (ObjectChangeApplyFunc) endsegment_change_apply;
1003 change->obj_change.revert = (ObjectChangeRevertFunc) endsegment_change_revert;
1004 change->obj_change.free = (ObjectChangeFreeFunc) endsegment_change_free;
1006 change->type = type;
1007 change->applied = 0;
1008 change->segment = segment;
1009 change->point = *point;
1010 change->handle = handle;
1011 if (segment == 0)
1012 change->old_end_handle = orth->handles[0];
1013 else
1014 change->old_end_handle = orth->handles[orth->numpoints-2];
1015 change->cp = change->old_end_handle->connected_to;
1017 return (ObjectChange *)change;
1021 static void
1022 midsegment_change_free(struct MidSegmentChange *change)
1024 if ( (change->type==TYPE_ADD_SEGMENT && !change->applied) ||
1025 (change->type==TYPE_REMOVE_SEGMENT && change->applied) ){
1026 if (change->handles[0])
1027 g_free(change->handles[0]);
1028 change->handles[0] = NULL;
1029 if (change->handles[1])
1030 g_free(change->handles[1]);
1031 change->handles[1] = NULL;
1035 static void
1036 midsegment_change_apply(struct MidSegmentChange *change, DiaObject *obj)
1038 OrthConn *orth = (OrthConn *)obj;
1039 change->applied = 1;
1041 switch (change->type) {
1042 case TYPE_ADD_SEGMENT:
1043 add_point(orth, change->segment+1, &change->points[1]);
1044 add_point(orth, change->segment+1, &change->points[0]);
1045 insert_handle(orth, change->segment+1, change->handles[1],
1046 orth->orientation[change->segment] );
1047 insert_handle(orth, change->segment+1, change->handles[0],
1048 FLIP_ORIENT(orth->orientation[change->segment]) );
1049 break;
1050 case TYPE_REMOVE_SEGMENT:
1051 delete_point(orth, change->segment);
1052 remove_handle(orth, change->segment);
1053 delete_point(orth, change->segment);
1054 remove_handle(orth, change->segment);
1055 if (orth->orientation[change->segment]==HORIZONTAL) {
1056 orth->points[change->segment].x = change->points[0].x;
1057 } else {
1058 orth->points[change->segment].y = change->points[0].y;
1060 break;
1064 static void
1065 midsegment_change_revert(struct MidSegmentChange *change, DiaObject *obj)
1067 OrthConn *orth = (OrthConn *)obj;
1069 switch (change->type) {
1070 case TYPE_ADD_SEGMENT:
1071 delete_point(orth, change->segment+1);
1072 remove_handle(orth, change->segment+1);
1073 delete_point(orth, change->segment+1);
1074 remove_handle(orth, change->segment+1);
1075 break;
1076 case TYPE_REMOVE_SEGMENT:
1077 if (orth->orientation[change->segment]==HORIZONTAL) {
1078 orth->points[change->segment].x = change->points[1].x;
1079 } else {
1080 orth->points[change->segment].y = change->points[1].y;
1082 add_point(orth, change->segment, &change->points[1]);
1083 add_point(orth, change->segment, &change->points[0]);
1084 insert_handle(orth, change->segment, change->handles[1],
1085 orth->orientation[change->segment-1] );
1086 insert_handle(orth, change->segment, change->handles[0],
1087 FLIP_ORIENT(orth->orientation[change->segment-1]) );
1088 break;
1090 change->applied = 0;
1093 static ObjectChange *
1094 midsegment_create_change(OrthConn *orth, enum change_type type,
1095 int segment,
1096 Point *point1, Point *point2,
1097 Handle *handle1, Handle *handle2)
1099 struct MidSegmentChange *change;
1101 change = g_new(struct MidSegmentChange, 1);
1103 change->obj_change.apply = (ObjectChangeApplyFunc) midsegment_change_apply;
1104 change->obj_change.revert = (ObjectChangeRevertFunc) midsegment_change_revert;
1105 change->obj_change.free = (ObjectChangeFreeFunc) midsegment_change_free;
1107 change->type = type;
1108 change->applied = 0;
1109 change->segment = segment;
1110 change->points[0] = *point1;
1111 change->points[1] = *point2;
1112 change->handles[0] = handle1;
1113 change->handles[1] = handle2;
1115 return (ObjectChange *)change;
1118 static void
1119 autoroute_change_free(struct AutorouteChange *change)
1121 g_free(change->points);
1124 static void
1125 autoroute_change_apply(struct AutorouteChange *change, DiaObject *obj)
1127 OrthConn *orth = (OrthConn*)obj;
1129 if (change->on) {
1130 orth->autorouting = TRUE;
1131 autoroute_layout_orthconn(orth, obj->handles[0]->connected_to,
1132 obj->handles[1]->connected_to);
1133 } else {
1134 orth->autorouting = FALSE;
1135 orthconn_set_points(orth, orth->numpoints, change->points);
1139 static void
1140 autoroute_change_revert(struct AutorouteChange *change, DiaObject *obj)
1142 OrthConn *orth = (OrthConn*)obj;
1144 if (change->on) {
1145 orth->autorouting = FALSE;
1146 orthconn_set_points(orth, orth->numpoints, change->points);
1147 } else {
1148 orth->autorouting = TRUE;
1149 autoroute_layout_orthconn(orth, obj->handles[0]->connected_to,
1150 obj->handles[1]->connected_to);
1154 static ObjectChange *
1155 autoroute_create_change(OrthConn *orth, gboolean on)
1157 struct AutorouteChange *change;
1158 int i;
1160 change = g_new(struct AutorouteChange, 1);
1162 change->obj_change.apply = (ObjectChangeApplyFunc) autoroute_change_apply;
1163 change->obj_change.revert = (ObjectChangeRevertFunc) autoroute_change_revert;
1164 change->obj_change.free = (ObjectChangeFreeFunc) autoroute_change_free;
1166 change->on = on;
1167 change->points = g_new(Point, orth->numpoints);
1168 for (i = 0; i < orth->numpoints; i++)
1169 change->points[i] = orth->points[i];
1171 return (ObjectChange *)change;
1174 ObjectChange *
1175 orthconn_toggle_autorouting_callback(DiaObject *obj, Point *clicked, gpointer data)
1177 ObjectChange *change;
1178 /* This is kinda hackish. Since we can't see the menu item, we have to
1179 * assume that we're right about toggling and just send !orth->autorouting.
1181 change = orthconn_set_autorouting((OrthConn*)obj,
1182 !((OrthConn*)obj)->autorouting);
1183 orthconn_update_data((OrthConn *)obj);
1184 return change;
1187 void
1188 orthconn_update_object_menu(OrthConn *orth, Point *clicked,
1189 DiaMenuItem *object_menu_items)
1191 object_menu_items[0].active = DIAMENU_ACTIVE|DIAMENU_TOGGLE|
1192 (orth->autorouting?DIAMENU_TOGGLE_ON:0);