* lib/text.h: Added text_get_line() declaration
[dia.git] / lib / neworth_conn.c
blobaaf59884f13959ab1b844d0cd16be0871a57c722
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"
34 #include "diarenderer.h"
36 static void place_handle_by_swapping(NewOrthConn *orth,
37 int index, Handle *handle);
39 enum change_type {
40 TYPE_ADD_SEGMENT,
41 TYPE_REMOVE_SEGMENT
44 static ObjectChange *
45 midsegment_create_change(NewOrthConn *orth, enum change_type type,
46 int segment,
47 Point *point1, Point *point2,
48 Handle *handle1, Handle *handle2);
50 struct MidSegmentChange {
51 ObjectChange obj_change;
53 /* All additions and deletions of segments in the middle
54 * of the NewOrthConn must delete/add two segments to keep
55 * the horizontal/vertical balance.
57 * None of the end segments must be removed by this change.
60 enum change_type type;
61 int applied;
63 int segment;
64 Point points[2];
65 Handle *handles[2]; /* These handles cannot be connected */
66 ConnectionPoint *conn; /* ? */
67 ObjectChange *cplchange[2];
70 static ObjectChange *
71 endsegment_create_change(NewOrthConn *orth, enum change_type type,
72 int segment, Point *point,
73 Handle *handle);
75 struct EndSegmentChange {
76 ObjectChange obj_change;
78 /* Additions and deletions of segments of at the endpoints
79 * of the NewOrthConn.
81 * Addition of an endpoint segment need not store any point.
82 * Deletion of an endpoint segment need to store the endpoint position
83 * so that it can be reverted.
84 * Deleted segments might be connected, so we must store the connection
85 * point.
88 enum change_type type;
89 int applied;
91 int segment;
92 Point point;
93 Handle *handle;
94 Handle *old_end_handle;
95 ConnectionPoint *cp; /* NULL in add segment and if not connected in
96 remove segment */
97 ObjectChange *cplchange;
101 static void set_midpoint(Point *point, NewOrthConn *orth, int segment)
103 int i = segment;
104 point->x = 0.5*(orth->points[i].x + orth->points[i+1].x);
105 point->y = 0.5*(orth->points[i].y + orth->points[i+1].y);
108 static void setup_midpoint_handle(Handle *handle)
110 handle->id = HANDLE_MIDPOINT;
111 handle->type = HANDLE_MINOR_CONTROL;
112 handle->connect_type = HANDLE_NONCONNECTABLE;
113 handle->connected_to = NULL;
116 static void setup_endpoint_handle(Handle *handle, HandleId id )
118 handle->id = id;
119 handle->type = HANDLE_MAJOR_CONTROL;
120 handle->connect_type = HANDLE_CONNECTABLE;
121 handle->connected_to = NULL;
124 static int get_handle_nr(NewOrthConn *orth, Handle *handle)
126 int i = 0;
127 for (i=0;i<orth->numpoints-1;i++) {
128 if (orth->handles[i] == handle)
129 return i;
131 return -1;
134 static int get_segment_nr(NewOrthConn *orth, Point *point, real max_dist)
136 int i;
137 int segment;
138 real distance, tmp_dist;
140 segment = 0;
141 distance = distance_line_point(&orth->points[0], &orth->points[1], 0, point);
143 for (i=1;i<orth->numpoints-1;i++) {
144 tmp_dist = distance_line_point(&orth->points[i], &orth->points[i+1], 0, point);
145 if (tmp_dist < distance) {
146 segment = i;
147 distance = tmp_dist;
151 if (distance < max_dist)
152 return segment;
154 return -1;
158 ObjectChange*
159 neworthconn_move_handle(NewOrthConn *orth, Handle *handle,
160 Point *to, ConnectionPoint *cp,
161 HandleMoveReason reason, ModifierKeys modifiers)
163 int n;
164 int handle_nr;
166 switch(handle->id) {
167 case HANDLE_MOVE_STARTPOINT:
168 orth->points[0] = *to;
169 switch (orth->orientation[0]) {
170 case HORIZONTAL:
171 orth->points[1].y = to->y;
172 break;
173 case VERTICAL:
174 orth->points[1].x = to->x;
175 break;
177 break;
178 case HANDLE_MOVE_ENDPOINT:
179 n = orth->numpoints - 1;
180 orth->points[n] = *to;
181 switch (orth->orientation[n-1]) {
182 case HORIZONTAL:
183 orth->points[n-1].y = to->y;
184 break;
185 case VERTICAL:
186 orth->points[n-1].x = to->x;
187 break;
189 break;
190 case HANDLE_MIDPOINT:
191 n = orth->numpoints - 1;
192 handle_nr = get_handle_nr(orth, handle);
194 switch (orth->orientation[handle_nr]) {
195 case HORIZONTAL:
196 orth->points[handle_nr].y = to->y;
197 orth->points[handle_nr+1].y = to->y;
198 break;
199 case VERTICAL:
200 orth->points[handle_nr].x = to->x;
201 orth->points[handle_nr+1].x = to->x;
202 break;
204 break;
205 default:
206 message_error("Internal error in neworthconn_move_handle.\n");
207 break;
210 return NULL;
213 ObjectChange*
214 neworthconn_move(NewOrthConn *orth, Point *to)
216 Point p;
217 int i;
219 p = *to;
220 point_sub(&p, &orth->points[0]);
222 orth->points[0] = *to;
223 for (i=1;i<orth->numpoints;i++) {
224 point_add(&orth->points[i], &p);
227 return NULL;
230 real
231 neworthconn_distance_from(NewOrthConn *orth, Point *point, real line_width)
233 int i;
234 real dist;
236 dist = distance_line_point( &orth->points[0], &orth->points[1],
237 line_width, point);
238 for (i=1;i<orth->numpoints-1;i++) {
239 dist = MIN(dist,
240 distance_line_point( &orth->points[i], &orth->points[i+1],
241 line_width, point));
243 return dist;
246 static void
247 neworthconn_update_midpoints(NewOrthConn *orth)
249 int i;
250 GSList *elem;
252 elem=orth->midpoints->connections;
254 /* Update connection points, using the handles' positions where useful : */
255 set_midpoint(&((ConnectionPoint *)(elem->data))->pos,orth,0);
256 elem=g_slist_next(elem);
257 for (i=1; i<orth->numpoints-2; i++) {
258 ((ConnectionPoint *)(elem->data))->pos = orth->handles[i]->pos;
259 elem = g_slist_next(elem);
261 set_midpoint(&(((ConnectionPoint *)(elem->data))->pos),orth,i);
267 static void
268 adjust_handle_count_to(NewOrthConn *orth, guint count) {
269 /* This will shrink or expand orth->handles as necessary (so that
270 orth->numhandles matches orth->numpoints-1, most probably), by adding or
271 removing minor handles and keeping the endpoint handles at the
272 extremities of the array. */
274 if (orth->numhandles == count) return;
275 if (orth->numhandles < count) { /* adding */
276 int i;
277 orth->handles = g_realloc(orth->handles,
278 (count)*sizeof(Handle *));
279 orth->handles[count-1] = orth->handles[orth->numhandles-1];
280 orth->handles[orth->numhandles-1] = NULL;
281 for (i=orth->numhandles-1; i<count-1; i++) {
282 Handle *handle = g_new0(Handle,1);
283 setup_midpoint_handle(handle);
284 object_add_handle(&orth->object,handle);
285 orth->handles[i] = handle;
287 } else { /* removing */
288 int i;
289 for (i=count-1; i<orth->numhandles-1; i++) {
290 Handle *handle = orth->handles[i];
291 object_remove_handle(&orth->object,handle);
292 g_free(handle);
293 orth->handles[i] = NULL;
295 orth->handles[count-1] = orth->handles[orth->numhandles-1];
296 orth->handles[orth->numhandles-1] = NULL;
297 orth->handles = g_realloc(orth->handles,
298 (count)*sizeof(Handle *));
300 orth->numhandles = count;
301 /* handles' positions will be set now */
305 void
306 neworthconn_update_data(NewOrthConn *orth)
308 int i;
309 DiaObject *obj = (DiaObject *)orth;
310 Point *points;
311 ConnectionPoint *start_cp;
312 ConnectionPoint *end_cp;
314 obj->position = orth->points[0];
316 /* During startup, handles may not have been setup yet, so do so
317 * temporarily to be able to get the last handle connection.
319 * [This looks like avoiding the symptom but not healing the
320 * root cause, but it appears to work for orth_conn.c since
321 * a while so do it here as well ... --hb]
323 adjust_handle_count_to(orth, orth->numpoints-1);
325 start_cp = orth->handles[0]->connected_to;
326 end_cp = orth->handles[orth->numpoints-2]->connected_to;
328 if (!orth->points) {
329 g_warning("This NewOrthConn object is very sick !");
330 return;
333 points = orth->points;
334 if (connpoint_is_autogap(start_cp) ||
335 connpoint_is_autogap(end_cp)) {
336 Point* new_points = g_new(Point, orth->numpoints);
337 int i;
338 for (i = 0; i < orth->numpoints; i++) {
339 new_points[i] = points[i];
342 if (connpoint_is_autogap(start_cp)) {
343 new_points[0] = calculate_object_edge(&start_cp->pos, &new_points[1],
344 start_cp->object);
345 printf("Moved start to %f, %f\n",
346 new_points[0].x, new_points[0].y);
348 if (connpoint_is_autogap(end_cp)) {
349 new_points[orth->numpoints-1] =
350 calculate_object_edge(&end_cp->pos, &new_points[orth->numpoints-2],
351 end_cp->object);
352 printf("Moved end to %f, %f\n",
353 new_points[orth->numpoints-1].x, new_points[orth->numpoints-1].y);
355 g_free(points);
356 orth->points = new_points;
359 obj->position = orth->points[0];
361 adjust_handle_count_to(orth,orth->numpoints-1);
362 connpointline_adjust_count(orth->midpoints,orth->numpoints-1,NULL);
364 /* Make sure start-handle is first and end-handle is second. */
365 place_handle_by_swapping(orth, 0, orth->handles[0]);
366 place_handle_by_swapping(orth, 1, orth->handles[orth->numpoints-2]);
368 /* Update handles : */
369 orth->handles[0]->pos = orth->points[0];
370 orth->handles[orth->numpoints-2]->pos = orth->points[orth->numpoints-1];
372 for (i=1;i<orth->numpoints-2;i++) {
373 set_midpoint(&orth->handles[i]->pos, orth, i);
375 neworthconn_update_midpoints(orth);
378 void
379 neworthconn_update_boundingbox(NewOrthConn *orth)
381 assert(orth != NULL);
383 polyline_bbox(&orth->points[0],
384 orth->numpoints,
385 &orth->extra_spacing, FALSE,
386 &orth->object.bounding_box);
389 void
390 neworthconn_simple_draw(NewOrthConn *orth, DiaRenderer *renderer, real width)
392 Point *points;
394 assert(orth != NULL);
395 assert(renderer != NULL);
397 if (!orth->points) {
398 g_warning("This NewOrthConn object is very sick !");
399 return;
402 points = &orth->points[0];
404 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
405 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
406 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_MITER);
407 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
409 DIA_RENDERER_GET_CLASS(renderer)->draw_polyline(renderer, points, orth->numpoints,
410 &color_black);
415 neworthconn_can_delete_segment(NewOrthConn *orth, Point *clickedpoint)
417 int segment;
419 /* Cannot delete any segments when there are only two left,
420 * and not amy middle segment if there are only three segments.
423 if (orth->numpoints==3)
424 return 0;
426 segment = get_segment_nr(orth, clickedpoint, 1.0);
428 if (segment<0)
429 return 0;
431 if ( (segment != 0) && (segment != orth->numpoints-2)) {
432 /* middle segment */
433 if (orth->numpoints==4)
434 return 0;
437 return 1;
441 neworthconn_can_add_segment(NewOrthConn *orth, Point *clickedpoint)
443 int segment = get_segment_nr(orth, clickedpoint, 1000000.0);
445 if (segment<0)
446 return 0;
448 return 1;
451 /* Needs to have at least 2 handles.
452 The handles are stored in order in the NewOrthConn, but need
453 not be stored in order in the DiaObject.handles array. This
454 is so that derived object can do what they want with
455 DiaObject.handles. */
457 void
458 neworthconn_init(NewOrthConn *orth, Point *startpoint)
460 DiaObject *obj;
462 obj = &orth->object;
464 object_init(obj, 3, 0);
466 orth->numpoints = 4;
467 orth->numorient = orth->numpoints - 1;
468 orth->numhandles = 3;
470 orth->points = g_malloc(4*sizeof(Point));
471 orth->orientation = g_malloc(3*sizeof(Orientation));
472 orth->handles = g_malloc(3*sizeof(Handle *));
474 orth->handles[0] = g_new(Handle, 1);
475 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
476 obj->handles[0] = orth->handles[0];
478 orth->handles[1] = g_new(Handle, 1);
479 setup_midpoint_handle(orth->handles[1]);
480 obj->handles[1] = orth->handles[1];
482 orth->handles[2] = g_new(Handle, 1);
483 setup_endpoint_handle(orth->handles[2], HANDLE_MOVE_ENDPOINT);
484 obj->handles[2] = orth->handles[2];
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 orth->midpoints = connpointline_create(obj,3);
501 neworthconn_update_data(orth);
504 void
505 neworthconn_copy(NewOrthConn *from, NewOrthConn *to)
507 int i,rcc;
508 DiaObject *toobj, *fromobj;
510 toobj = &to->object;
511 fromobj = &from->object;
513 object_copy(fromobj, toobj);
515 to->numpoints = from->numpoints;
516 to->numorient = from->numorient;
517 to->numhandles = from->numhandles;
519 to->points = g_malloc((to->numpoints)*sizeof(Point));
521 for (i=0;i<to->numpoints;i++) {
522 to->points[i] = from->points[i];
525 to->orientation = g_malloc((to->numpoints-1)*sizeof(Orientation));
526 to->handles = g_malloc((to->numpoints-1)*sizeof(Handle *));
528 for (i=0;i<to->numpoints-1;i++) {
529 to->orientation[i] = from->orientation[i];
530 to->handles[i] = g_new(Handle,1);
531 *to->handles[i] = *from->handles[i];
532 to->handles[i]->connected_to = NULL;
533 toobj->handles[i] = to->handles[i];
535 rcc = 0;
536 to->midpoints = connpointline_copy(toobj,from->midpoints,&rcc);
537 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
541 void
542 neworthconn_destroy(NewOrthConn *orth)
544 int i;
546 connpointline_destroy(orth->midpoints);
547 object_destroy(&orth->object);
549 g_free(orth->points);
550 g_free(orth->orientation);
552 for (i=0;i<orth->numpoints-1;i++) {
553 g_free(orth->handles[i]);
555 g_free(orth->handles);
558 static void
559 place_handle_by_swapping(NewOrthConn *orth, int index, Handle *handle)
561 DiaObject *obj;
562 Handle *tmp;
563 int j;
565 obj = (DiaObject *)orth;
566 if (obj->handles[index] == handle)
567 return; /* Nothing to do */
569 for (j=0;j<obj->num_handles;j++) {
570 if (obj->handles[j] == handle) {
571 /* Swap handle j and index */
572 tmp = obj->handles[j];
573 obj->handles[j] = obj->handles[index];
574 obj->handles[index] = tmp;
576 return;
581 void
582 neworthconn_save(NewOrthConn *orth, ObjectNode obj_node)
584 int i;
585 AttributeNode attr;
587 /* Make sure start-handle is first and end-handle is second. */
588 place_handle_by_swapping(orth, 0, orth->handles[0]);
589 place_handle_by_swapping(orth, 1, orth->handles[orth->numpoints-2]);
591 object_save(&orth->object, obj_node);
593 attr = new_attribute(obj_node, "orth_points");
595 for (i=0;i<orth->numpoints;i++) {
596 data_add_point(attr, &orth->points[i]);
599 attr = new_attribute(obj_node, "orth_orient");
600 for (i=0;i<orth->numpoints-1;i++) {
601 data_add_enum(attr, orth->orientation[i]);
605 void
606 neworthconn_load(NewOrthConn *orth, ObjectNode obj_node) /* NOTE: Does object_init() */
608 int i;
609 AttributeNode attr;
610 DataNode data;
611 int n;
613 DiaObject *obj = &orth->object;
615 object_load(obj, obj_node);
617 attr = object_find_attribute(obj_node, "orth_points");
619 if (attr != NULL)
620 orth->numpoints = attribute_num_data(attr);
621 else
622 orth->numpoints = 0;
624 object_init(obj, orth->numpoints-1,0);
626 orth->numorient = orth->numpoints - 1;
628 data = attribute_first_data(attr);
629 orth->points = g_malloc((orth->numpoints)*sizeof(Point));
630 for (i=0;i<orth->numpoints;i++) {
631 data_point(data, &orth->points[i]);
632 data = data_next(data);
635 attr = object_find_attribute(obj_node, "orth_orient");
637 data = attribute_first_data(attr);
638 orth->orientation = g_malloc((orth->numpoints-1)*sizeof(Orientation));
639 for (i=0;i<orth->numpoints-1;i++) {
640 orth->orientation[i] = data_enum(data);
641 data = data_next(data);
644 orth->handles = g_malloc((orth->numpoints-1)*sizeof(Handle *));
646 orth->handles[0] = g_new(Handle, 1);
647 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
648 orth->handles[0]->pos = orth->points[0];
649 obj->handles[0] = orth->handles[0];
651 n = orth->numpoints-2;
652 orth->handles[n] = g_new(Handle, 1);
653 setup_endpoint_handle(orth->handles[n], HANDLE_MOVE_ENDPOINT);
654 orth->handles[n]->pos = orth->points[orth->numpoints-1];
655 obj->handles[1] = orth->handles[n];
657 for (i=1; i<orth->numpoints-2; i++) {
658 orth->handles[i] = g_new(Handle, 1);
659 setup_midpoint_handle(orth->handles[i]);
660 obj->handles[i+1] = orth->handles[i];
662 orth->numhandles = orth->numpoints-1;
663 orth->midpoints = connpointline_create(obj,orth->numpoints-1);
665 neworthconn_update_data(orth);
668 Handle*
669 neworthconn_get_middle_handle( NewOrthConn *orth )
671 int n = orth->numpoints - 1 ;
672 return orth->handles[ n/2 ] ;
675 ObjectChange *
676 neworthconn_delete_segment(NewOrthConn *orth, Point *clickedpoint)
678 int segment;
679 ObjectChange *change = NULL;
681 if (orth->numpoints==3)
682 return NULL;
684 segment = get_segment_nr(orth, clickedpoint, 1.0);
685 if (segment < 0)
686 return NULL;
688 if (segment==0) {
689 change = endsegment_create_change(orth, TYPE_REMOVE_SEGMENT, segment,
690 &orth->points[segment],
691 orth->handles[segment]);
692 } else if (segment == orth->numpoints-2) {
693 change = endsegment_create_change(orth, TYPE_REMOVE_SEGMENT, segment,
694 &orth->points[segment+1],
695 orth->handles[segment]);
696 } else if (segment > 0) {
697 /* Don't delete the last midpoint segment.
698 * That would delete also the endpoint segment after it.
700 if (segment == orth->numpoints-3)
701 segment--;
703 change = midsegment_create_change(orth, TYPE_REMOVE_SEGMENT, segment,
704 &orth->points[segment],
705 &orth->points[segment+1],
706 orth->handles[segment],
707 orth->handles[segment+1]);
710 change->apply(change, (DiaObject *)orth);
712 return change;
715 ObjectChange *
716 neworthconn_add_segment(NewOrthConn *orth, Point *clickedpoint)
718 Handle *handle1, *handle2;
719 ObjectChange *change = NULL;
720 int segment;
721 Point newpoint;
723 segment = get_segment_nr(orth, clickedpoint, 1.0);
724 if (segment < 0)
725 return NULL;
727 if (segment==0) { /* First segment */
728 handle1 = g_new(Handle, 1);
729 setup_endpoint_handle(handle1, HANDLE_MOVE_STARTPOINT);
730 change = endsegment_create_change(orth, TYPE_ADD_SEGMENT,
731 0, &orth->points[0],
732 handle1);
733 } else if (segment == orth->numpoints-2) { /* Last segment */
734 handle1 = g_new(Handle, 1);
735 setup_endpoint_handle(handle1, HANDLE_MOVE_ENDPOINT);
736 change = endsegment_create_change(orth, TYPE_ADD_SEGMENT,
737 segment+1, &orth->points[segment+1],
738 handle1);
739 } else if (segment > 0) {
740 handle1 = g_new(Handle, 1);
741 setup_midpoint_handle(handle1);
742 handle2 = g_new(Handle, 1);
743 setup_midpoint_handle(handle2);
744 newpoint = *clickedpoint;
745 if (orth->orientation[segment]==HORIZONTAL)
746 newpoint.y = orth->points[segment].y;
747 else
748 newpoint.x = orth->points[segment].x;
750 change = midsegment_create_change(orth, TYPE_ADD_SEGMENT, segment,
751 &newpoint,
752 &newpoint,
753 handle1,
754 handle2);
757 change->apply(change, (DiaObject *)orth);
759 return change;
762 static void
763 delete_point(NewOrthConn *orth, int pos)
765 int i;
767 orth->numpoints--;
768 orth->numorient = orth->numpoints - 1;
770 for (i=pos;i<orth->numpoints;i++) {
771 orth->points[i] = orth->points[i+1];
774 orth->points = g_realloc(orth->points, orth->numpoints*sizeof(Point));
777 /* Make sure numpoints have been decreased before calling this function.
778 * ie. call delete_point first.
780 static void
781 remove_handle(NewOrthConn *orth, int segment)
783 int i;
784 Handle *handle;
786 handle = orth->handles[segment];
788 for (i=segment; i < orth->numpoints-1; i++) {
789 orth->handles[i] = orth->handles[i+1];
790 orth->orientation[i] = orth->orientation[i+1];
793 orth->orientation = g_realloc(orth->orientation,
794 (orth->numpoints-1)*sizeof(Orientation));
795 orth->handles = g_realloc(orth->handles,
796 (orth->numpoints-1)*sizeof(Handle *));
798 object_remove_handle(&orth->object, handle);
799 orth->numhandles = orth->numpoints-1;
803 static void
804 add_point(NewOrthConn *orth, int pos, Point *point)
806 int i;
808 orth->numpoints++;
809 orth->numorient = orth->numpoints-1;
811 orth->points = g_realloc(orth->points, orth->numpoints*sizeof(Point));
812 for (i=orth->numpoints-1;i>pos;i--) {
813 orth->points[i] = orth->points[i-1];
815 orth->points[pos] = *point;
818 /* Make sure numpoints have been increased before calling this function.
819 * ie. call add_point first.
821 static void
822 insert_handle(NewOrthConn *orth, int segment,
823 Handle *handle, Orientation orient)
825 int i;
827 orth->orientation = g_realloc(orth->orientation,
828 (orth->numpoints-1)*sizeof(Orientation));
829 orth->handles = g_realloc(orth->handles,
830 (orth->numpoints-1)*sizeof(Handle *));
831 for (i=orth->numpoints-2;i>segment;i--) {
832 orth->handles[i] = orth->handles[i-1];
833 orth->orientation[i] = orth->orientation[i-1];
835 orth->handles[segment] = handle;
836 orth->orientation[segment] = orient;
838 object_add_handle(&orth->object, handle);
839 orth->numhandles = orth->numpoints-1;
844 static void
845 endsegment_change_free(struct EndSegmentChange *change)
847 if ( (change->type==TYPE_ADD_SEGMENT && !change->applied) ||
848 (change->type==TYPE_REMOVE_SEGMENT && change->applied) ){
849 if (change->handle)
850 g_free(change->handle);
851 change->handle = NULL;
853 if (change->cplchange) {
854 if (change->cplchange->free) change->cplchange->free(change->cplchange);
855 g_free(change->cplchange);
856 change->cplchange = NULL;
860 static void
861 endsegment_change_apply(struct EndSegmentChange *change, DiaObject *obj)
863 NewOrthConn *orth = (NewOrthConn *)obj;
865 change->applied = 1;
867 switch (change->type) {
868 case TYPE_ADD_SEGMENT:
869 object_unconnect(obj, change->old_end_handle);
870 if (change->segment==0) { /* first */
871 add_point(orth, 0, &change->point);
872 insert_handle(orth, change->segment,
873 change->handle, FLIP_ORIENT(orth->orientation[0]) );
874 setup_midpoint_handle(orth->handles[1]);
875 obj->position = orth->points[0];
876 change->cplchange = connpointline_add_point(orth->midpoints,
877 &change->point);
878 } else { /* last */
879 add_point(orth, orth->numpoints, &change->point);
880 insert_handle(orth, change->segment, change->handle,
881 FLIP_ORIENT(orth->orientation[orth->numpoints-3]) );
882 setup_midpoint_handle(orth->handles[orth->numpoints-3]);
883 change->cplchange = connpointline_add_point(orth->midpoints,
884 &orth->midpoints->end);
886 break;
887 case TYPE_REMOVE_SEGMENT:
888 object_unconnect(obj, change->old_end_handle);
889 change->cplchange =
890 connpointline_remove_point(orth->midpoints,
891 &orth->points[change->segment]);
892 if (change->segment==0) { /* first */
893 delete_point(orth, 0);
894 remove_handle(orth, 0);
895 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
896 obj->position = orth->points[0];
897 } else { /* last */
898 delete_point(orth, orth->numpoints-1);
899 remove_handle(orth, change->segment);
900 setup_endpoint_handle(orth->handles[orth->numpoints-2],
901 HANDLE_MOVE_ENDPOINT);
903 break;
905 neworthconn_update_midpoints(orth); /* useless ? */
908 static void
909 endsegment_change_revert(struct EndSegmentChange *change, DiaObject *obj)
911 NewOrthConn *orth = (NewOrthConn *)obj;
913 change->cplchange->revert(change->cplchange,obj);
914 switch (change->type) {
915 case TYPE_ADD_SEGMENT:
916 object_unconnect(obj, change->handle);
917 if (change->segment==0) { /* first */
918 delete_point(orth, 0);
919 remove_handle(orth, 0);
920 setup_endpoint_handle(orth->handles[0], HANDLE_MOVE_STARTPOINT);
921 obj->position = orth->points[0];
922 } else { /* last */
923 delete_point(orth, orth->numpoints-1);
924 remove_handle(orth, change->segment);
925 setup_endpoint_handle(orth->handles[orth->numpoints-2],
926 HANDLE_MOVE_ENDPOINT);
928 break;
929 case TYPE_REMOVE_SEGMENT:
930 if (change->segment==0) { /* first */
931 add_point(orth, 0, &change->point);
932 insert_handle(orth, change->segment,
933 change->handle, FLIP_ORIENT(orth->orientation[0]) );
934 setup_midpoint_handle(orth->handles[1]);
935 obj->position = orth->points[0];
936 } else { /* last */
937 add_point(orth, orth->numpoints, &change->point);
938 insert_handle(orth, change->segment, change->handle,
939 FLIP_ORIENT(orth->orientation[orth->numpoints-3]) );
940 setup_midpoint_handle(orth->handles[orth->numpoints-3]);
942 break;
944 change->applied = 0;
945 neworthconn_update_midpoints(orth); /* useless ? */
948 static ObjectChange *
949 endsegment_create_change(NewOrthConn *orth, enum change_type type,
950 int segment, Point *point,
951 Handle *handle)
953 struct EndSegmentChange *change;
955 change = g_new0(struct EndSegmentChange, 1);
957 change->obj_change.apply = (ObjectChangeApplyFunc) endsegment_change_apply;
958 change->obj_change.revert = (ObjectChangeRevertFunc) endsegment_change_revert;
959 change->obj_change.free = (ObjectChangeFreeFunc) endsegment_change_free;
961 change->type = type;
962 change->applied = 0;
963 change->segment = segment;
964 change->point = *point;
965 change->handle = handle;
966 if (segment == 0)
967 change->old_end_handle = orth->handles[0];
968 else
969 change->old_end_handle = orth->handles[orth->numpoints-2];
970 change->cp = change->old_end_handle->connected_to;
971 return (ObjectChange *)change;
975 static void
976 midsegment_change_free(struct MidSegmentChange *change)
978 if ( (change->type==TYPE_ADD_SEGMENT && !change->applied) ||
979 (change->type==TYPE_REMOVE_SEGMENT && change->applied) ){
980 if (change->handles[0])
981 g_free(change->handles[0]);
982 change->handles[0] = NULL;
983 if (change->handles[1])
984 g_free(change->handles[1]);
985 change->handles[1] = NULL;
988 if (change->cplchange[0]) {
989 if (change->cplchange[0]->free)
990 change->cplchange[0]->free(change->cplchange[0]);
991 g_free(change->cplchange[0]);
992 change->cplchange[0] = NULL;
994 if (change->cplchange[1]) {
995 if (change->cplchange[1]->free)
996 change->cplchange[1]->free(change->cplchange[1]);
997 g_free(change->cplchange[1]);
998 change->cplchange[1] = NULL;
1002 static void
1003 midsegment_change_apply(struct MidSegmentChange *change, DiaObject *obj)
1005 NewOrthConn *orth = (NewOrthConn *)obj;
1006 int seg;
1008 change->applied = 1;
1010 switch (change->type) {
1011 case TYPE_ADD_SEGMENT:
1012 add_point(orth, change->segment+1, &change->points[1]);
1013 add_point(orth, change->segment+1, &change->points[0]);
1014 insert_handle(orth, change->segment+1, change->handles[1],
1015 orth->orientation[change->segment] );
1016 insert_handle(orth, change->segment+1, change->handles[0],
1017 FLIP_ORIENT(orth->orientation[change->segment]) );
1018 change->cplchange[0] =
1019 connpointline_add_point(orth->midpoints,&change->points[0]);
1020 change->cplchange[1] =
1021 connpointline_add_point(orth->midpoints,&change->points[1]);
1022 break;
1023 case TYPE_REMOVE_SEGMENT:
1024 seg = change->segment?change->segment:1;
1025 change->cplchange[0] =
1026 connpointline_remove_point(orth->midpoints,
1027 &orth->points[seg-1]);
1028 change->cplchange[1] =
1029 connpointline_remove_point(orth->midpoints,
1030 &orth->points[seg]);
1031 delete_point(orth, change->segment);
1032 remove_handle(orth, change->segment);
1033 delete_point(orth, change->segment);
1034 remove_handle(orth, change->segment);
1035 if (orth->orientation[change->segment]==HORIZONTAL) {
1036 orth->points[change->segment].x = change->points[0].x;
1037 } else {
1038 orth->points[change->segment].y = change->points[0].y;
1040 break;
1042 neworthconn_update_midpoints(orth); /* useless ? */
1045 static void
1046 midsegment_change_revert(struct MidSegmentChange *change, DiaObject *obj)
1048 NewOrthConn *orth = (NewOrthConn *)obj;
1050 change->cplchange[0]->revert(change->cplchange[0],obj);
1051 change->cplchange[1]->revert(change->cplchange[1],obj);
1053 switch (change->type) {
1054 case TYPE_ADD_SEGMENT:
1055 delete_point(orth, change->segment+1);
1056 remove_handle(orth, change->segment+1);
1057 delete_point(orth, change->segment+1);
1058 remove_handle(orth, change->segment+1);
1059 break;
1060 case TYPE_REMOVE_SEGMENT:
1061 if (orth->orientation[change->segment]==HORIZONTAL) {
1062 orth->points[change->segment].x = change->points[1].x;
1063 } else {
1064 orth->points[change->segment].y = change->points[1].y;
1066 add_point(orth, change->segment, &change->points[1]);
1067 add_point(orth, change->segment, &change->points[0]);
1068 insert_handle(orth, change->segment, change->handles[1],
1069 orth->orientation[change->segment-1] );
1070 insert_handle(orth, change->segment, change->handles[0],
1071 FLIP_ORIENT(orth->orientation[change->segment-1]) );
1072 break;
1074 change->applied = 0;
1077 static ObjectChange *
1078 midsegment_create_change(NewOrthConn *orth, enum change_type type,
1079 int segment,
1080 Point *point1, Point *point2,
1081 Handle *handle1, Handle *handle2)
1083 struct MidSegmentChange *change;
1085 change = g_new(struct MidSegmentChange, 1);
1087 change->obj_change.apply = (ObjectChangeApplyFunc) midsegment_change_apply;
1088 change->obj_change.revert = (ObjectChangeRevertFunc) midsegment_change_revert;
1089 change->obj_change.free = (ObjectChangeFreeFunc) midsegment_change_free;
1091 change->type = type;
1092 change->applied = 0;
1093 change->segment = segment;
1094 change->points[0] = *point1;
1095 change->points[1] = *point2;
1096 change->handles[0] = handle1;
1097 change->handles[1] = handle2;
1099 return (ObjectChange *)change;