#ifdef'd xmlerror.h
[dia.git] / lib / neworth_conn.c
blob3fe065696f145c0df960b4130d13b2814edcfcdf
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This file forked ; added a connection point in the middle of each
5 * segment. C.Chepelov, january 2000.
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.
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.
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.
21 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <math.h>
27 #include <string.h> /* memcpy() */
29 #include "neworth_conn.h"
30 #include "connectionpoint.h"
31 #include "message.h"
32 #include "diamenu.h"
33 #include "handle.h"
35 static void place_handle_by_swapping(NewOrthConn *orth,
36 int index, Handle *handle);
38 enum change_type {
39 TYPE_ADD_SEGMENT,
40 TYPE_REMOVE_SEGMENT
43 static ObjectChange *
44 midsegment_create_change(NewOrthConn *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 NewOrthConn 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 */
65 ConnectionPoint *conn; /* ? */
66 ObjectChange *cplchange[2];
69 static ObjectChange *
70 endsegment_create_change(NewOrthConn *orth, enum change_type type,
71 int segment, Point *point,
72 Handle *handle);
74 struct EndSegmentChange {
75 ObjectChange obj_change;
77 /* Additions and deletions of segments of at the endpoints
78 * of the NewOrthConn.
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 */
96 ObjectChange *cplchange;
100 static void set_midpoint(Point *point, NewOrthConn *orth, int segment)
102 int i = segment;
103 point->x = 0.5*(orth->points[i].x + orth->points[i+1].x);
104 point->y = 0.5*(orth->points[i].y + orth->points[i+1].y);
107 static void setup_midpoint_handle(Handle *handle)
109 handle->id = HANDLE_MIDPOINT;
110 handle->type = HANDLE_MINOR_CONTROL;
111 handle->connect_type = HANDLE_NONCONNECTABLE;
112 handle->connected_to = NULL;
115 static void setup_endpoint_handle(Handle *handle, HandleId id )
117 handle->id = id;
118 handle->type = HANDLE_MAJOR_CONTROL;
119 handle->connect_type = HANDLE_CONNECTABLE;
120 handle->connected_to = NULL;
123 static int get_handle_nr(NewOrthConn *orth, Handle *handle)
125 int i = 0;
126 for (i=0;i<orth->numpoints-1;i++) {
127 if (orth->handles[i] == handle)
128 return i;
130 return -1;
133 static int get_segment_nr(NewOrthConn *orth, Point *point, real max_dist)
135 int i;
136 int segment;
137 real distance, tmp_dist;
139 segment = 0;
140 distance = distance_line_point(&orth->points[0], &orth->points[1], 0, point);
142 for (i=1;i<orth->numpoints-1;i++) {
143 tmp_dist = distance_line_point(&orth->points[i], &orth->points[i+1], 0, point);
144 if (tmp_dist < distance) {
145 segment = i;
146 distance = tmp_dist;
150 if (distance < max_dist)
151 return segment;
153 return -1;
157 void
158 neworthconn_move_handle(NewOrthConn *orth, Handle *handle,
159 Point *to, HandleMoveReason reason)
161 int n;
162 int handle_nr;
164 switch(handle->id) {
165 case HANDLE_MOVE_STARTPOINT:
166 orth->points[0] = *to;
167 switch (orth->orientation[0]) {
168 case HORIZONTAL:
169 orth->points[1].y = to->y;
170 break;
171 case VERTICAL:
172 orth->points[1].x = to->x;
173 break;
175 break;
176 case HANDLE_MOVE_ENDPOINT:
177 n = orth->numpoints - 1;
178 orth->points[n] = *to;
179 switch (orth->orientation[n-1]) {
180 case HORIZONTAL:
181 orth->points[n-1].y = to->y;
182 break;
183 case VERTICAL:
184 orth->points[n-1].x = to->x;
185 break;
187 break;
188 case HANDLE_MIDPOINT:
189 n = orth->numpoints - 1;
190 handle_nr = get_handle_nr(orth, handle);
192 switch (orth->orientation[handle_nr]) {
193 case HORIZONTAL:
194 orth->points[handle_nr].y = to->y;
195 orth->points[handle_nr+1].y = to->y;
196 break;
197 case VERTICAL:
198 orth->points[handle_nr].x = to->x;
199 orth->points[handle_nr+1].x = to->x;
200 break;
202 break;
203 default:
204 message_error("Internal error in neworthconn_move_handle.\n");
205 break;
209 void
210 neworthconn_move(NewOrthConn *orth, Point *to)
212 Point p;
213 int i;
215 p = *to;
216 point_sub(&p, &orth->points[0]);
218 orth->points[0] = *to;
219 for (i=1;i<orth->numpoints;i++) {
220 point_add(&orth->points[i], &p);
224 real
225 neworthconn_distance_from(NewOrthConn *orth, Point *point, real line_width)
227 int i;
228 real dist;
230 dist = distance_line_point( &orth->points[0], &orth->points[1],
231 line_width, point);
232 for (i=1;i<orth->numpoints-1;i++) {
233 dist = MIN(dist,
234 distance_line_point( &orth->points[i], &orth->points[i+1],
235 line_width, point));
237 return dist;
240 static void
241 neworthconn_update_midpoints(NewOrthConn *orth)
243 int i;
244 GSList *elem;
246 elem=orth->midpoints->connections;
248 /* Update connection points, using the handles' positions where useful : */
249 set_midpoint(&((ConnectionPoint *)(elem->data))->pos,orth,0);
250 elem=g_slist_next(elem);
251 for (i=1; i<orth->numpoints-2; i++) {
252 ((ConnectionPoint *)(elem->data))->pos = orth->handles[i]->pos;
253 elem = g_slist_next(elem);
255 set_midpoint(&(((ConnectionPoint *)(elem->data))->pos),orth,i);
261 static void
262 adjust_handle_count_to(NewOrthConn *orth, guint count) {
263 /* This will shrink or expand orth->handles as necessary (so that
264 orth->numhandles matches orth->numpoints-1, most probably), by adding or
265 removing minor handles and keeping the endpoint handles at the
266 extremities of the array. */
268 if (orth->numhandles == count) return;
269 if (orth->numhandles < count) { /* adding */
270 int i;
271 orth->handles = g_realloc(orth->handles,
272 (count)*sizeof(Handle *));
273 orth->handles[count-1] = orth->handles[orth->numhandles-1];
274 orth->handles[orth->numhandles-1] = NULL;
275 for (i=orth->numhandles-1; i<count-1; i++) {
276 Handle *handle = g_new0(Handle,1);
277 setup_midpoint_handle(handle);
278 object_add_handle(&orth->object,handle);
279 orth->handles[i] = handle;
281 } else { /* removing */
282 int i;
283 for (i=count-1; i<orth->numhandles-1; i++) {
284 Handle *handle = orth->handles[i];
285 object_remove_handle(&orth->object,handle);
286 g_free(handle);
287 orth->handles[i] = NULL;
289 orth->handles[count-1] = orth->handles[orth->numhandles-1];
290 orth->handles[orth->numhandles-1] = NULL;
291 orth->handles = g_realloc(orth->handles,
292 (count)*sizeof(Handle *));
294 orth->numhandles = count;
295 /* handles' positions will be set now */
299 void
300 neworthconn_update_data(NewOrthConn *orth)
302 int i;
303 Object *obj = (Object *)orth;
305 if (!orth->points) {
306 g_warning("This NewOrthConn object is very sick !");
307 return;
309 obj->position = orth->points[0];
311 adjust_handle_count_to(orth,orth->numpoints-1);
312 connpointline_adjust_count(orth->midpoints,orth->numpoints-1,NULL);
314 /* Make sure start-handle is first and end-handle is second. */
315 place_handle_by_swapping(orth, 0, orth->handles[0]);
316 place_handle_by_swapping(orth, 1, orth->handles[orth->numpoints-2]);
318 /* Update handles : */
319 orth->handles[0]->pos = orth->points[0];
320 orth->handles[orth->numpoints-2]->pos = orth->points[orth->numpoints-1];
322 for (i=1;i<orth->numpoints-2;i++) {
323 set_midpoint(&orth->handles[i]->pos, orth, i);
325 neworthconn_update_midpoints(orth);
328 void
329 neworthconn_update_boundingbox(NewOrthConn *orth)
331 assert(orth != NULL);
333 polyline_bbox(&orth->points[0],
334 orth->numpoints,
335 &orth->extra_spacing, FALSE,
336 &orth->object.bounding_box);
339 void
340 neworthconn_simple_draw(NewOrthConn *orth, Renderer *renderer, real width)
342 Point *points;
344 assert(orth != NULL);
345 assert(renderer != NULL);
347 if (!orth->points) {
348 g_warning("This NewOrthConn object is very sick !");
349 return;
352 points = &orth->points[0];
354 renderer->ops->set_linewidth(renderer, width);
355 renderer->ops->set_linestyle(renderer, LINESTYLE_SOLID);
356 renderer->ops->set_linejoin(renderer, LINEJOIN_MITER);
357 renderer->ops->set_linecaps(renderer, LINECAPS_BUTT);
359 renderer->ops->draw_polyline(renderer, points, orth->numpoints,
360 &color_black);
365 neworthconn_can_delete_segment(NewOrthConn *orth, Point *clickedpoint)
367 int segment;
369 /* Cannot delete any segments when there are only two left,
370 * and not amy middle segment if there are only three segments.
373 if (orth->numpoints==3)
374 return 0;
376 segment = get_segment_nr(orth, clickedpoint, 1.0);
378 if (segment<0)
379 return 0;
381 if ( (segment != 0) && (segment != orth->numpoints-2)) {
382 /* middle segment */
383 if (orth->numpoints==4)
384 return 0;
387 return 1;
391 neworthconn_can_add_segment(NewOrthConn *orth, Point *clickedpoint)
393 int segment = get_segment_nr(orth, clickedpoint, 1000000.0);
395 if (segment<0)
396 return 0;
398 return 1;
401 /* Needs to have at least 2 handles.
402 The handles are stored in order in the NewOrthConn, but need
403 not be stored in order in the Object.handles array. This
404 is so that derived object can do what they want with
405 Object.handles. */
407 void
408 neworthconn_init(NewOrthConn *orth, Point *startpoint)
410 Object *obj;
412 obj = &orth->object;
414 object_init(obj, 3, 0);
416 orth->numpoints = 4;
417 orth->numorient = orth->numpoints - 1;
418 orth->numhandles = 3;
420 orth->points = g_malloc(4*sizeof(Point));
421 orth->orientation = g_malloc(3*sizeof(Orientation));
422 orth->handles = g_malloc(3*sizeof(Handle *));
424 orth->handles[0] = g_new(Handle, 1);
425 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
426 obj->handles[0] = orth->handles[0];
428 orth->handles[1] = g_new(Handle, 1);
429 setup_midpoint_handle(orth->handles[1]);
430 obj->handles[1] = orth->handles[1];
432 orth->handles[2] = g_new(Handle, 1);
433 setup_endpoint_handle(orth->handles[2], HANDLE_MOVE_ENDPOINT);
434 obj->handles[2] = orth->handles[2];
436 /* Just so we have some position: */
437 orth->points[0] = *startpoint;
438 orth->points[1].x = startpoint->x;
439 orth->points[1].y = startpoint->y + 1.0;
440 orth->points[2].x = startpoint->x + 1.0;
441 orth->points[2].y = startpoint->y + 1.0;
442 orth->points[3].x = startpoint->x + 2.0;
443 orth->points[3].y = startpoint->y + 1.0;
445 orth->orientation[0] = VERTICAL;
446 orth->orientation[1] = HORIZONTAL;
447 orth->orientation[2] = VERTICAL;
449 orth->midpoints = connpointline_create(obj,3);
451 neworthconn_update_data(orth);
454 void
455 neworthconn_copy(NewOrthConn *from, NewOrthConn *to)
457 int i,rcc;
458 Object *toobj, *fromobj;
460 toobj = &to->object;
461 fromobj = &from->object;
463 object_copy(fromobj, toobj);
465 to->numpoints = from->numpoints;
466 to->numorient = from->numorient;
467 to->numhandles = from->numhandles;
469 to->points = g_malloc((to->numpoints)*sizeof(Point));
471 for (i=0;i<to->numpoints;i++) {
472 to->points[i] = from->points[i];
475 to->orientation = g_malloc((to->numpoints-1)*sizeof(Orientation));
476 to->handles = g_malloc((to->numpoints-1)*sizeof(Handle *));
478 for (i=0;i<to->numpoints-1;i++) {
479 to->orientation[i] = from->orientation[i];
480 to->handles[i] = g_new(Handle,1);
481 *to->handles[i] = *from->handles[i];
482 to->handles[i]->connected_to = NULL;
483 toobj->handles[i] = to->handles[i];
485 rcc = 0;
486 to->midpoints = connpointline_copy(toobj,from->midpoints,&rcc);
487 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
491 void
492 neworthconn_destroy(NewOrthConn *orth)
494 int i;
496 connpointline_destroy(orth->midpoints);
497 object_destroy(&orth->object);
499 g_free(orth->points);
500 g_free(orth->orientation);
502 for (i=0;i<orth->numpoints-1;i++) {
503 g_free(orth->handles[i]);
505 g_free(orth->handles);
508 static void
509 place_handle_by_swapping(NewOrthConn *orth, int index, Handle *handle)
511 Object *obj;
512 Handle *tmp;
513 int j;
515 obj = (Object *)orth;
516 if (obj->handles[index] == handle)
517 return; /* Nothing to do */
519 for (j=0;j<obj->num_handles;j++) {
520 if (obj->handles[j] == handle) {
521 /* Swap handle j and index */
522 tmp = obj->handles[j];
523 obj->handles[j] = obj->handles[index];
524 obj->handles[index] = tmp;
526 return;
531 void
532 neworthconn_save(NewOrthConn *orth, ObjectNode obj_node)
534 int i;
535 AttributeNode attr;
537 /* Make sure start-handle is first and end-handle is second. */
538 place_handle_by_swapping(orth, 0, orth->handles[0]);
539 place_handle_by_swapping(orth, 1, orth->handles[orth->numpoints-2]);
541 object_save(&orth->object, obj_node);
543 attr = new_attribute(obj_node, "orth_points");
545 for (i=0;i<orth->numpoints;i++) {
546 data_add_point(attr, &orth->points[i]);
549 attr = new_attribute(obj_node, "orth_orient");
550 for (i=0;i<orth->numpoints-1;i++) {
551 data_add_enum(attr, orth->orientation[i]);
555 void
556 neworthconn_load(NewOrthConn *orth, ObjectNode obj_node) /* NOTE: Does object_init() */
558 int i;
559 AttributeNode attr;
560 DataNode data;
561 int n;
563 Object *obj = &orth->object;
565 object_load(obj, obj_node);
567 attr = object_find_attribute(obj_node, "orth_points");
569 if (attr != NULL)
570 orth->numpoints = attribute_num_data(attr);
571 else
572 orth->numpoints = 0;
574 object_init(obj, orth->numpoints-1,0);
576 orth->numorient = orth->numpoints - 1;
578 data = attribute_first_data(attr);
579 orth->points = g_malloc((orth->numpoints)*sizeof(Point));
580 for (i=0;i<orth->numpoints;i++) {
581 data_point(data, &orth->points[i]);
582 data = data_next(data);
585 attr = object_find_attribute(obj_node, "orth_orient");
587 data = attribute_first_data(attr);
588 orth->orientation = g_malloc((orth->numpoints-1)*sizeof(Orientation));
589 for (i=0;i<orth->numpoints-1;i++) {
590 orth->orientation[i] = data_enum(data);
591 data = data_next(data);
594 orth->handles = g_malloc((orth->numpoints-1)*sizeof(Handle *));
596 orth->handles[0] = g_new(Handle, 1);
597 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
598 orth->handles[0]->pos = orth->points[0];
599 obj->handles[0] = orth->handles[0];
601 n = orth->numpoints-2;
602 orth->handles[n] = g_new(Handle, 1);
603 setup_endpoint_handle(orth->handles[n], HANDLE_MOVE_ENDPOINT);
604 orth->handles[n]->pos = orth->points[orth->numpoints-1];
605 obj->handles[1] = orth->handles[n];
607 for (i=1; i<orth->numpoints-2; i++) {
608 orth->handles[i] = g_new(Handle, 1);
609 setup_midpoint_handle(orth->handles[i]);
610 obj->handles[i+1] = orth->handles[i];
612 orth->numhandles = orth->numpoints-1;
613 orth->midpoints = connpointline_create(obj,orth->numpoints-1);
615 neworthconn_update_data(orth);
618 Handle*
619 neworthconn_get_middle_handle( NewOrthConn *orth )
621 int n = orth->numpoints - 1 ;
622 return orth->handles[ n/2 ] ;
625 ObjectChange *
626 neworthconn_delete_segment(NewOrthConn *orth, Point *clickedpoint)
628 int segment;
629 ObjectChange *change = NULL;
631 if (orth->numpoints==3)
632 return NULL;
634 segment = get_segment_nr(orth, clickedpoint, 1.0);
635 if (segment < 0)
636 return NULL;
638 if (segment==0) {
639 change = endsegment_create_change(orth, TYPE_REMOVE_SEGMENT, segment,
640 &orth->points[segment],
641 orth->handles[segment]);
642 } else if (segment == orth->numpoints-2) {
643 change = endsegment_create_change(orth, TYPE_REMOVE_SEGMENT, segment,
644 &orth->points[segment+1],
645 orth->handles[segment]);
646 } else if (segment > 0) {
647 /* Don't delete the last midpoint segment.
648 * That would delete also the endpoint segment after it.
650 if (segment == orth->numpoints-3)
651 segment--;
653 change = midsegment_create_change(orth, TYPE_REMOVE_SEGMENT, segment,
654 &orth->points[segment],
655 &orth->points[segment+1],
656 orth->handles[segment],
657 orth->handles[segment+1]);
660 change->apply(change, (Object *)orth);
662 return change;
665 ObjectChange *
666 neworthconn_add_segment(NewOrthConn *orth, Point *clickedpoint)
668 Handle *handle1, *handle2;
669 ObjectChange *change = NULL;
670 int segment;
671 Point newpoint;
673 segment = get_segment_nr(orth, clickedpoint, 1.0);
674 if (segment < 0)
675 return NULL;
677 if (segment==0) { /* First segment */
678 handle1 = g_new(Handle, 1);
679 setup_endpoint_handle(handle1, HANDLE_MOVE_STARTPOINT);
680 change = endsegment_create_change(orth, TYPE_ADD_SEGMENT,
681 0, &orth->points[0],
682 handle1);
683 } else if (segment == orth->numpoints-2) { /* Last segment */
684 handle1 = g_new(Handle, 1);
685 setup_endpoint_handle(handle1, HANDLE_MOVE_ENDPOINT);
686 change = endsegment_create_change(orth, TYPE_ADD_SEGMENT,
687 segment+1, &orth->points[segment+1],
688 handle1);
689 } else if (segment > 0) {
690 handle1 = g_new(Handle, 1);
691 setup_midpoint_handle(handle1);
692 handle2 = g_new(Handle, 1);
693 setup_midpoint_handle(handle2);
694 newpoint = *clickedpoint;
695 if (orth->orientation[segment]==HORIZONTAL)
696 newpoint.y = orth->points[segment].y;
697 else
698 newpoint.x = orth->points[segment].x;
700 change = midsegment_create_change(orth, TYPE_ADD_SEGMENT, segment,
701 &newpoint,
702 &newpoint,
703 handle1,
704 handle2);
707 change->apply(change, (Object *)orth);
709 return change;
712 static void
713 delete_point(NewOrthConn *orth, int pos)
715 int i;
717 orth->numpoints--;
718 orth->numorient = orth->numpoints - 1;
720 for (i=pos;i<orth->numpoints;i++) {
721 orth->points[i] = orth->points[i+1];
724 orth->points = g_realloc(orth->points, orth->numpoints*sizeof(Point));
727 /* Make sure numpoints have been decreased before calling this function.
728 * ie. call delete_point first.
730 static void
731 remove_handle(NewOrthConn *orth, int segment)
733 int i;
734 Handle *handle;
736 handle = orth->handles[segment];
738 for (i=segment; i < orth->numpoints-1; i++) {
739 orth->handles[i] = orth->handles[i+1];
740 orth->orientation[i] = orth->orientation[i+1];
743 orth->orientation = g_realloc(orth->orientation,
744 (orth->numpoints-1)*sizeof(Orientation));
745 orth->handles = g_realloc(orth->handles,
746 (orth->numpoints-1)*sizeof(Handle *));
748 object_remove_handle(&orth->object, handle);
749 orth->numhandles = orth->numpoints-1;
753 static void
754 add_point(NewOrthConn *orth, int pos, Point *point)
756 int i;
758 orth->numpoints++;
759 orth->numorient = orth->numpoints-1;
761 orth->points = g_realloc(orth->points, orth->numpoints*sizeof(Point));
762 for (i=orth->numpoints-1;i>pos;i--) {
763 orth->points[i] = orth->points[i-1];
765 orth->points[pos] = *point;
768 /* Make sure numpoints have been increased before calling this function.
769 * ie. call add_point first.
771 static void
772 insert_handle(NewOrthConn *orth, int segment,
773 Handle *handle, Orientation orient)
775 int i;
777 orth->orientation = g_realloc(orth->orientation,
778 (orth->numpoints-1)*sizeof(Orientation));
779 orth->handles = g_realloc(orth->handles,
780 (orth->numpoints-1)*sizeof(Handle *));
781 for (i=orth->numpoints-2;i>segment;i--) {
782 orth->handles[i] = orth->handles[i-1];
783 orth->orientation[i] = orth->orientation[i-1];
785 orth->handles[segment] = handle;
786 orth->orientation[segment] = orient;
788 object_add_handle(&orth->object, handle);
789 orth->numhandles = orth->numpoints-1;
794 static void
795 endsegment_change_free(struct EndSegmentChange *change)
797 if ( (change->type==TYPE_ADD_SEGMENT && !change->applied) ||
798 (change->type==TYPE_REMOVE_SEGMENT && change->applied) ){
799 if (change->handle)
800 g_free(change->handle);
801 change->handle = NULL;
803 if (change->cplchange) {
804 if (change->cplchange->free) change->cplchange->free(change->cplchange);
805 g_free(change->cplchange);
806 change->cplchange = NULL;
810 static void
811 endsegment_change_apply(struct EndSegmentChange *change, Object *obj)
813 NewOrthConn *orth = (NewOrthConn *)obj;
815 change->applied = 1;
817 switch (change->type) {
818 case TYPE_ADD_SEGMENT:
819 object_unconnect(obj, change->old_end_handle);
820 if (change->segment==0) { /* first */
821 add_point(orth, 0, &change->point);
822 insert_handle(orth, change->segment,
823 change->handle, FLIP_ORIENT(orth->orientation[0]) );
824 setup_midpoint_handle(orth->handles[1]);
825 obj->position = orth->points[0];
826 change->cplchange = connpointline_add_point(orth->midpoints,
827 &change->point);
828 } else { /* last */
829 add_point(orth, orth->numpoints, &change->point);
830 insert_handle(orth, change->segment, change->handle,
831 FLIP_ORIENT(orth->orientation[orth->numpoints-3]) );
832 setup_midpoint_handle(orth->handles[orth->numpoints-3]);
833 change->cplchange = connpointline_add_point(orth->midpoints,
834 &orth->midpoints->end);
836 break;
837 case TYPE_REMOVE_SEGMENT:
838 object_unconnect(obj, change->old_end_handle);
839 change->cplchange =
840 connpointline_remove_point(orth->midpoints,
841 &orth->points[change->segment]);
842 if (change->segment==0) { /* first */
843 delete_point(orth, 0);
844 remove_handle(orth, 0);
845 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
846 obj->position = orth->points[0];
847 } else { /* last */
848 delete_point(orth, orth->numpoints-1);
849 remove_handle(orth, change->segment);
850 setup_endpoint_handle(orth->handles[orth->numpoints-2],
851 HANDLE_MOVE_ENDPOINT);
853 break;
855 neworthconn_update_midpoints(orth); /* useless ? */
858 static void
859 endsegment_change_revert(struct EndSegmentChange *change, Object *obj)
861 NewOrthConn *orth = (NewOrthConn *)obj;
863 change->cplchange->revert(change->cplchange,obj);
864 switch (change->type) {
865 case TYPE_ADD_SEGMENT:
866 object_unconnect(obj, change->handle);
867 if (change->segment==0) { /* first */
868 delete_point(orth, 0);
869 remove_handle(orth, 0);
870 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
871 obj->position = orth->points[0];
872 } else { /* last */
873 delete_point(orth, orth->numpoints-1);
874 remove_handle(orth, change->segment);
875 setup_endpoint_handle(orth->handles[orth->numpoints-2],
876 HANDLE_MOVE_ENDPOINT);
878 break;
879 case TYPE_REMOVE_SEGMENT:
880 if (change->segment==0) { /* first */
881 add_point(orth, 0, &change->point);
882 insert_handle(orth, change->segment,
883 change->handle, FLIP_ORIENT(orth->orientation[0]) );
884 setup_midpoint_handle(orth->handles[1]);
885 obj->position = orth->points[0];
886 } else { /* last */
887 add_point(orth, orth->numpoints, &change->point);
888 insert_handle(orth, change->segment, change->handle,
889 FLIP_ORIENT(orth->orientation[orth->numpoints-3]) );
890 setup_midpoint_handle(orth->handles[orth->numpoints-3]);
892 break;
894 change->applied = 0;
895 neworthconn_update_midpoints(orth); /* useless ? */
898 static ObjectChange *
899 endsegment_create_change(NewOrthConn *orth, enum change_type type,
900 int segment, Point *point,
901 Handle *handle)
903 struct EndSegmentChange *change;
905 change = g_new0(struct EndSegmentChange, 1);
907 change->obj_change.apply = (ObjectChangeApplyFunc) endsegment_change_apply;
908 change->obj_change.revert = (ObjectChangeRevertFunc) endsegment_change_revert;
909 change->obj_change.free = (ObjectChangeFreeFunc) endsegment_change_free;
911 change->type = type;
912 change->applied = 0;
913 change->segment = segment;
914 change->point = *point;
915 change->handle = handle;
916 if (segment == 0)
917 change->old_end_handle = orth->handles[0];
918 else
919 change->old_end_handle = orth->handles[orth->numpoints-2];
920 change->cp = change->old_end_handle->connected_to;
921 return (ObjectChange *)change;
925 static void
926 midsegment_change_free(struct MidSegmentChange *change)
928 if ( (change->type==TYPE_ADD_SEGMENT && !change->applied) ||
929 (change->type==TYPE_REMOVE_SEGMENT && change->applied) ){
930 if (change->handles[0])
931 g_free(change->handles[0]);
932 change->handles[0] = NULL;
933 if (change->handles[1])
934 g_free(change->handles[1]);
935 change->handles[1] = NULL;
938 if (change->cplchange[0]) {
939 if (change->cplchange[0]->free)
940 change->cplchange[0]->free(change->cplchange[0]);
941 g_free(change->cplchange[0]);
942 change->cplchange[0] = NULL;
944 if (change->cplchange[1]) {
945 if (change->cplchange[1]->free)
946 change->cplchange[1]->free(change->cplchange[1]);
947 g_free(change->cplchange[1]);
948 change->cplchange[1] = NULL;
952 static void
953 midsegment_change_apply(struct MidSegmentChange *change, Object *obj)
955 NewOrthConn *orth = (NewOrthConn *)obj;
956 int seg;
958 change->applied = 1;
960 switch (change->type) {
961 case TYPE_ADD_SEGMENT:
962 add_point(orth, change->segment+1, &change->points[1]);
963 add_point(orth, change->segment+1, &change->points[0]);
964 insert_handle(orth, change->segment+1, change->handles[1],
965 orth->orientation[change->segment] );
966 insert_handle(orth, change->segment+1, change->handles[0],
967 FLIP_ORIENT(orth->orientation[change->segment]) );
968 change->cplchange[0] =
969 connpointline_add_point(orth->midpoints,&change->points[0]);
970 change->cplchange[1] =
971 connpointline_add_point(orth->midpoints,&change->points[1]);
972 break;
973 case TYPE_REMOVE_SEGMENT:
974 seg = change->segment?change->segment:1;
975 change->cplchange[0] =
976 connpointline_remove_point(orth->midpoints,
977 &orth->points[seg-1]);
978 change->cplchange[1] =
979 connpointline_remove_point(orth->midpoints,
980 &orth->points[seg]);
981 delete_point(orth, change->segment);
982 remove_handle(orth, change->segment);
983 delete_point(orth, change->segment);
984 remove_handle(orth, change->segment);
985 if (orth->orientation[change->segment]==HORIZONTAL) {
986 orth->points[change->segment].x = change->points[0].x;
987 } else {
988 orth->points[change->segment].y = change->points[0].y;
990 break;
992 neworthconn_update_midpoints(orth); /* useless ? */
995 static void
996 midsegment_change_revert(struct MidSegmentChange *change, Object *obj)
998 NewOrthConn *orth = (NewOrthConn *)obj;
1000 change->cplchange[0]->revert(change->cplchange[0],obj);
1001 change->cplchange[1]->revert(change->cplchange[1],obj);
1003 switch (change->type) {
1004 case TYPE_ADD_SEGMENT:
1005 delete_point(orth, change->segment+1);
1006 remove_handle(orth, change->segment+1);
1007 delete_point(orth, change->segment+1);
1008 remove_handle(orth, change->segment+1);
1009 break;
1010 case TYPE_REMOVE_SEGMENT:
1011 if (orth->orientation[change->segment]==HORIZONTAL) {
1012 orth->points[change->segment].x = change->points[1].x;
1013 } else {
1014 orth->points[change->segment].y = change->points[1].y;
1016 add_point(orth, change->segment, &change->points[1]);
1017 add_point(orth, change->segment, &change->points[0]);
1018 insert_handle(orth, change->segment, change->handles[1],
1019 orth->orientation[change->segment-1] );
1020 insert_handle(orth, change->segment, change->handles[0],
1021 FLIP_ORIENT(orth->orientation[change->segment-1]) );
1022 break;
1024 change->applied = 0;
1027 static ObjectChange *
1028 midsegment_create_change(NewOrthConn *orth, enum change_type type,
1029 int segment,
1030 Point *point1, Point *point2,
1031 Handle *handle1, Handle *handle2)
1033 struct MidSegmentChange *change;
1035 change = g_new(struct MidSegmentChange, 1);
1037 change->obj_change.apply = (ObjectChangeApplyFunc) midsegment_change_apply;
1038 change->obj_change.revert = (ObjectChangeRevertFunc) midsegment_change_revert;
1039 change->obj_change.free = (ObjectChangeFreeFunc) midsegment_change_free;
1041 change->type = type;
1042 change->applied = 0;
1043 change->segment = segment;
1044 change->points[0] = *point1;
1045 change->points[1] = *point2;
1046 change->handles[0] = handle1;
1047 change->handles[1] = handle2;
1049 return (ObjectChange *)change;