1 /* object.c  */
2 /* COPYRIGHT (C) 2000 THE VICTORIA UNIVERSITY OF MANCHESTER and John Levon
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307, USA.
16  */
17 /* functions for dealing with Objects  */
18 /*
19  * $Log: object.c,v $
20  * Revision 1.4  2000/12/17 00:57:42  moz
21  * examples, filled open splines, highlight_objects
22  *
23  * Revision 1.3  2000/12/06 20:56:03  moz
24  * GPL stuff.
25  *
26  * Revision 1.2  2000/08/21 01:30:31  moz
27  * Re-calculate cache on spline enlarge to avoid low-res artefacts.
28  *
29  * Revision 1.1.1.1  2000/08/21 01:05:31  moz
30  *
31  *
32  * Revision 1.1.1.1  2000/07/19 22:45:30  moz
33  * CVS Import
34  *
35  * Revision 1.43  2000/03/13 01:50:12  moz
36  * Fixed add_object0() bug.
37  *
38  * Revision 1.42  2000/03/12 23:33:49  moz
39  * Fixed stupid typo reversing text lines !
40  *
41  * Revision 1.41  2000/03/12 04:13:40  moz
42  * Derry to node ellipses correctly.
43  *
44  * Revision 1.40  2000/03/11 19:10:15  moz
45  * Fix calc to recalc_text_box().
46  *
47  * Revision 1.39  2000/03/11 18:19:50  moz
48  * Adjust text box position to stay static on scale.
49  *
50  * Revision 1.38  2000/03/06 02:28:42  moz
51  * Compile fixes.
52  *
53  * Revision 1.37  2000/02/23 02:20:41  moz
54  * Was forgetting to NULL polyline.pic !.
55  *
56  * Revision 1.36  2000/02/21 03:53:13  moz
57  * Backed out debugging message.
58  *
59  * Revision 1.35  2000/02/18 21:14:15  moz
60  * polyline.pic fix.
61  * Some compile fixes.
62  *
63  * Revision 1.34  2000/02/18 04:06:27  moz
64  * Was accessing freed mem in remove_derry() ...
65  *
66  * Revision 1.33  2000/02/18 03:04:57  moz
67  * kill_object() should free derries.
68  *
69  * Revision 1.32  2000/02/17 22:25:56  moz
70  * Backed out debugging change.
71  *
72  * Revision 1.31  2000/02/04 02:17:38  moz
73  * kill_object() should free arcellipse arrows.
74  *
75  * Revision 1.30  2000/02/04 01:54:59  moz
76  * switch_icons() needs view
77  *
78  * Revision 1.29  2000/01/29 23:51:37  moz
79  * Small fix.
80  *
81  * Revision 1.28  2000/01/29 04:13:16  moz
82  * Removed more wrong v_error().
83  *
84  * Revision 1.27  2000/01/29 03:58:37  moz
85  * Default for get_nearest_point isn't an error.
86  *
87  * Revision 1.26  2000/01/26 18:50:23  moz
88  * kill_object should free arrows.
89  *
90  * Revision 1.25  2000/01/26 18:19:53  moz
91  * Changes to allow FIG_DEBUG_LIST_ALLOC.
92  *
93  * Revision 1.24  1999/11/29 20:34:40  moz
94  * BBSIZE() renamed to avoid IRIX param.h namespace clash.
95  *
96  * Revision 1.23  1999/11/15 02:06:34  moz
97  * Use rounded trig.
98  * Pick smallest object under cursor.
99  *
100  * Revision 1.22  1999/08/17 00:35:24  moz
101  * Fix malloc_node.
102  *
103  * Revision 1.21  1999/08/08 20:55:05  moz
104  * From clean up of structs.
105  *
106  * Revision 1.20  1999/07/29 20:44:35  moz
107  * Make sure rottext resize rubber box is redrawn by apply_scale...().
108  *
109  * Revision 1.19  1999/07/04 15:19:49  moz
110  * Only draw scaled compounds when finished.
111  *
112  * Revision 1.18  1999/07/04 03:13:59  moz
113  * Changes to derrying to an ellipse.
114  *
115  * Revision 1.17  1999/05/31 23:05:18  moz
116  * Spline curves contribute interpolated/angular points to attachment.
117  *
118  * Revision 1.16  1999/05/22 02:52:56  moz
119  * Correctly recalculate a scaled compound's bounding box.
120  *
121  * Revision 1.15  1999/05/19 17:08:37  moz
122  * 1.0 Checkin.
123  *
124  * Revision 1.14  1999/05/05 00:38:05  moz
125  * Derry to arcellipses.
126  *
127  * Revision 1.13  1999/05/04 20:09:57  moz
128  * Remove derries on rotate and scale.
129  *
130  * Revision 1.12  1999/05/04 20:01:45  moz
131  * Move targetee.
132  *
133  * Revision 1.11  1999/05/04 15:50:17  moz
134  * Merged in duplicate_object() and kill_object().
135  *
136  * Revision 1.10  1999/05/03 05:54:49  moz
137  * Scale text in compound.
138  *
139  * Revision 1.9  1999/04/27 16:58:26  moz
140  * Set text angle to zero if close (3 degrees).
141  *
142  * Revision 1.8  1999/04/27 16:55:59  moz
143  * Flip object.
144  *
145  * Revision 1.7  1999/04/27 04:11:33  moz
146  * Use calc_text_outer_box.
147  * -Wall appeasement.
148  *
149  * Revision 1.6  1999/04/26 19:59:55  moz
150  * move_object function added.
151  *
152  * Revision 1.5  1999/04/25 19:39:37  moz
153  * Rotate text's bounding box.
154  *
155  * Revision 1.4  1999/04/22 22:16:46  moz
156  * Don't register undos for scaling subobjects.
157  *
158  * Revision 1.3  1999/04/22 20:55:18  moz
159  * Rescale compounds within compounds correctly.
160  *
161  * Revision 1.2  1999/04/22 16:16:44  moz
162  * Imperfect fix for scaling arc circles.
163  *
164  * Revision 1.1  1999/03/30 00:05:37  moz
165  * Initial revision
166  *
167  */
168 
169 #include <math.h>
170 #include <string.h>
171 #include "include/figurine.h"
172 #include "include/extern.h"
173 
174 Tree malloc_node(void);
175 
176 /* this calculates the correct position for a scaled object with a tied corner  */
177 /* given a topleft corner, returns a new one  */
178 void
corner_magic(Object * ob,long * x,long * y,double rx,double ry)179 corner_magic(Object *ob, long *x, long *y, double rx, double ry)
180 {
181 	switch (state.tied_corner)
182 		{
183 		case TOPLEFT:
184 			break;
185 
186 		case TOPRIGHT:
187 				*x = *x + (ob->bbox.x2-ob->bbox.x1) - R(ob->bbox.x2-ob->bbox.x1,abs(rx));
188 			break;
189 
190 		case BOTTOMLEFT:
191 				*y = *y + (ob->bbox.y2-ob->bbox.y1) - R(ob->bbox.y2-ob->bbox.y1,abs(ry));
192 			break;
193 
194 		case BOTTOMRIGHT:
195 				*x = *x + (ob->bbox.x2-ob->bbox.x1) - R(ob->bbox.x2-ob->bbox.x1,abs(rx));
196 				*y = *y + (ob->bbox.y2-ob->bbox.y1) - R(ob->bbox.y2-ob->bbox.y1,abs(ry));
197 			break;
198 
199 		default:
200 			v_error(state.tied_corner);
201 			break;
202 		};
203 
204 }
205 
206 Tree
malloc_node(void)207 malloc_node(void)
208 {
209 	return (Tree)malloc(sizeof(Node));
210 }
211 
212 /* add an object to object tree and list  */
213 Tree
add_object(Tree t,List * l,Object * ob)214 add_object(Tree t, List *l, Object *ob)
215 {
216 	if ((t=add_object0(t,ob))!=NULL)
217 		*l = add_to_list(*l,(ulong)ob,ob->type,(void *)ob);
218 	return t;
219 }
220 
221 /* insert an object in object tree  */
222 Tree
add_object0(Tree t,Object * ob)223 add_object0(Tree t, Object *ob)
224 {
225 	Tree tp,tp2;
226 
227 	if (t==NULL) /* empty tree  */
228 		{
229 		tp = malloc_node();
230 		if (tp==NULL)
231 			return NULL;
232 
233 		tp->left = NULL;
234 		tp->right = NULL;
235 		tp->ob = ob;
236 		tp->bbox = ob->bbox;
237 		tp->up = NULL;
238 
239 		return tp;
240 		}
241 	else if (t->ob!=NULL) /* node */
242 		{
243 		tp = malloc_node();
244 		if (tp==NULL)
245 			return NULL;
246 		tp->left = NULL;
247 		tp->right = NULL;
248 		tp->ob = ob;
249 		tp->bbox = ob->bbox;
250 
251 		tp2 = malloc_node();
252 		if (tp2==NULL)
253 			return NULL;
254 		tp2->left = t; /* this is used in algorithm ! */
255 		tp2->right = tp;
256 		tp2->up = NULL;
257 		tp->up = tp2;
258 		t->up = tp2;
259 		tp2->ob = NULL;
260 		tp2->bbox = merge_boxes(tp2->left->bbox, tp2->right->bbox);
261 
262 		return tp2;
263 		}
264 	else
265 		{
266 		/* not at a node yet  */
267 		/* if no recursing */
268 		if (!is_contained_v(t->left->bbox,ob->bbox) && !is_contained_v(t->right->bbox,ob->bbox))
269 			{
270 			tp = malloc_node();
271 			if (tp==NULL)
272 				return NULL;
273 			tp->left = NULL;
274 			tp->right = NULL;
275 			tp->ob = ob;
276 			tp->bbox = ob->bbox;
277 
278 			tp2 = malloc_node();
279 			if (tp2==NULL)
280 				return NULL;
281 
282 			if ( (FIG_BBSIZE(merge_boxes(t->left->bbox,ob->bbox))-FIG_BBSIZE(t->left->bbox)) <
283 			   (FIG_BBSIZE(merge_boxes(t->right->bbox,ob->bbox))-FIG_BBSIZE(t->right->bbox)) )
284 				{
285 				tp2->left = t->left;
286 				tp2->right = tp;
287 				tp->up = tp2;
288 				t->left->up = tp2;
289 				tp2->ob = NULL;
290 				tp2->bbox = merge_boxes(tp2->left->bbox,tp2->right->bbox);
291 				t->left = tp2;
292 				tp2->up = t;
293 				t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
294 				}
295 			else
296 				{
297 				tp2->left = t->right;
298 				tp2->right = tp;
299 				tp->up = tp2;
300 				t->right->up = tp2;
301 				tp2->ob = NULL;
302 				tp2->bbox = merge_boxes(tp2->left->bbox,tp2->right->bbox);
303 				t->right = tp2;
304 				tp2->up = t;
305 				t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
306 				};
307 
308 			 	return t;
309 			}
310 		else if (!is_contained_v(t->left->bbox,t->right->bbox) &&
311 					!is_contained_v(t->right->bbox,t->left->bbox))
312 			{
313 			/* now ob is contained with in left or right, and they are disjointish, so recurse down */
314 			if (is_contained_v(t->left->bbox,ob->bbox))
315 				{
316 				t->left = add_object0(t->left,ob);
317 				t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
318 				t->left->up = t;
319 				return t;
320 				}
321 			else
322 				{
323 				t->right = add_object0(t->right,ob);
324 				t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
325 				t->right->up = t;
326 				return t;
327 				};
328 			}
329 		else if (is_contained_v(t->left->bbox,t->right->bbox))
330 			{
331 			/* now right is inside left, so join right with ob */
332 			if (is_contained_v(t->right->bbox,ob->bbox))
333 				{
334 				t->right = add_object0(t->right,ob);
335 				t->bbox = merge_boxes(t->left->bbox, t->right->bbox);
336 				t->right->up = t;
337 				return t;
338 				}
339 			else
340 				{
341 				tp = malloc_node();
342 				if (tp==NULL)
343 					return NULL;
344 				tp->left = NULL;
345 				tp->right = NULL;
346 				tp->ob = ob;
347 				tp->bbox = ob->bbox;
348 				tp2 = malloc_node();
349 				if (tp2==NULL)
350 					return NULL;
351 				tp2->left = tp;
352 				tp2->right = t->right;
353 				tp->up = tp2;
354 				t->right->up = tp2;
355 				tp2->ob = NULL;
356 				tp2->bbox = merge_boxes(tp2->left->bbox,tp2->right->bbox);
357 				t->right = tp2;
358 				tp2->up = t;
359 				t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
360 				return t;
361 				};
362 			}
363 		else
364 			{
365 			/* now left is inside right, so join left with ob */
366 			if (is_contained_v(t->left->bbox,ob->bbox))
367 				{
368 				t->left = add_object0(t->left,ob);
369 				t->bbox = merge_boxes(t->left->bbox, t->right->bbox);
370 				t->left->up = t;
371 				return t;
372 				}
373 			else
374 				{
375 				tp = malloc_node();
376 				if (tp==NULL)
377 					return NULL;
378 				tp->left = NULL;
379 				tp->right = NULL;
380 				tp->ob = ob;
381 				tp->bbox = ob->bbox;
382 				tp2 = malloc_node();
383 				if (tp2==NULL)
384 					return NULL;
385 				tp2->left = tp;
386 				tp2->right = t->left;
387 				tp->up = tp2;
388 				t->left->up = tp2;
389 				tp2->ob = NULL;
390 				tp2->bbox = merge_boxes(tp2->left->bbox,tp2->right->bbox);
391 				t->left = tp2;
392 				tp2->up = t;
393 				t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
394 				return t;
395 				};
396 
397 			};
398 		};
399 
400 }
401 
402 /* return leaf of tree t containing ob  */
403 Node *
get_object_node(Tree t,Object * ob)404 get_object_node(Tree t, Object *ob)
405 {
406 	Node *t2;
407 
408 	if (t==NULL)
409 		return NULL;
410 
411 	if (t->left==NULL && t->right==NULL)
412 		{
413 		if (ob==t->ob)
414 			return t;
415 		else
416 			return NULL;
417 		};
418 
419 	if ((t2=get_object_node(t->left,ob))!=NULL)
420 		return t2;
421 
422 	if ((t2=get_object_node(t->right,ob))!=NULL)
423 		return t2;
424 
425 	return NULL;
426 }
427 
428 /* find an object by its ticket value  */
429 /* this is used for derrying support  */
430 /* node ellipses must be special-cased */
431 Object *
get_object_by_ticket(Tree t,ulong ticket)432 get_object_by_ticket(Tree t, ulong ticket)
433 {
434 	Object *ob;
435 
436 	if (t==NULL)
437 		return NULL;
438 
439 	if (t->left==NULL && t->right==NULL)
440 		{
441 		if (ticket==t->ob->ticket)
442 			return t->ob;
443 		else if (t->ob->type==TEXT && t->ob->ob.text.node && ticket==t->ob->ob.text.ellipse->ticket)
444 			return t->ob->ob.text.ellipse;
445 		else
446 			return NULL;
447 		};
448 
449 	if ((ob=get_object_by_ticket(t->left,ticket))!=NULL)
450 		return ob;
451 
452 	if ((ob=get_object_by_ticket(t->right,ticket))!=NULL)
453 		return ob;
454 
455 	return NULL;
456 }
457 
458 /* take object off tree and list  */
459 Tree
trash_object(Tree t,List * lo,Object * ob)460 trash_object(Tree t, List *lo, Object *ob)
461 {
462 	t = delete_object(t,ob);
463 	*lo = delete_from_list(*lo, (ulong)ob);
464 	return t;
465 }
466 
467 /* take object off tree  */
468 Tree
delete_object(Tree t,Object * ob)469 delete_object(Tree t, Object *ob)
470 {
471 	Node *a;
472 
473 	if (t==NULL)
474 		return NULL;
475 
476 	if (t->left==NULL && t->right==NULL)
477 		{
478 		if (t->ob==ob)
479 			{
480 			free(t);
481 			return NULL;
482 			}
483 		else
484 			return t;
485 		};
486 
487 	if (t->left->ob!=NULL)
488 		{
489 		/* left of tree  */
490 		if (t->left->ob==ob)
491 			{
492 			a = t->right;
493 			t->right->up = NULL;
494 			free(t->left);
495 			free(t);
496 			return a;
497 			};
498 		};
499 
500 	if (t->right->ob!=NULL)
501 		{
502 		/* right of tree  */
503 		if (t->right->ob==ob)
504 			{
505 			a = t->left;
506 			t->left->up = NULL;
507 			free(t->right);
508 			free(t);
509 			return a;
510 			};
511 		};
512 
513 	if (t->left->ob==NULL)
514 		{
515 		t->left = delete_object(t->left,ob);
516 		t->left->up = t;
517 		};
518 
519 	if (t->right->ob==NULL)
520 		{
521 		t->right = delete_object(t->right,ob);
522 		t->right->up = t;
523 		};
524 
525 	t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
526 	return t;
527 }
528 
529 /* for small bbox changes, just propogate changes upwards  */
530 void
resize_bbox(Tree t,Object * ob)531 resize_bbox(Tree t, Object *ob)
532 {
533 	if (t==NULL)
534 		return;
535 
536 	if (t->left==NULL && t->right==NULL)
537 		{
538 		if (t->ob->ticket==ob->ticket)
539 			t->bbox = ob->bbox;
540 
541 		return;
542 		}
543 	else
544 		{
545 		resize_bbox(t->left,ob);
546 
547 		resize_bbox(t->right,ob);
548 
549 		t->bbox = merge_boxes(t->left->bbox,t->right->bbox);
550 		return;
551 		};
552 }
553 
554 /* for bbox changes, we need to reinsert to maintain a good tree  */
555 Tree
change_bbox(Tree t,Object * ob)556 change_bbox(Tree t, Object *ob)
557 {
558 	t = delete_object(t,ob);
559 	t = add_object0(t,ob);
560 	return t;
561 }
562 
563 Node *
object_at_point(View * v,Tree t,long x,long y)564 object_at_point(View *v, Tree t, long x, long y)
565 {
566 	List match=NULL;
567 	List l;
568 	Node *best=NULL;
569 	ulong bsize=ULONG_MAX;
570 
571 	match = object_at_point0(match, v, t, x, y);
572 	l = match;
573 
574 	while (match!=NULL)
575 		{
576 		if (FIG_BBSIZE(((Node *)match->data)->ob->bbox) < bsize)
577 			{
578 			bsize = FIG_BBSIZE(((Node *)match->data)->ob->bbox);
579 			best = (Node *)match->data;
580 			};
581 		match=match->next;
582 		};
583 
584 	delete_list(l);
585 	return best;
586 
587 
588 }
589 
590 /* return object at document coords x,y  */
591 List
object_at_point0(List match,View * v,Tree t,long x,long y)592 object_at_point0(List match, View *v, Tree t, long x, long y)
593 {
594 	if (t==NULL)
595 		return match;
596 
597 	if (t->left==NULL && t->right==NULL)
598 		{
599 		if (is_in_bbox(x,y,t->bbox.x1-P2D(SELECT_PIXEL_LOOSENESS,v),
600 								 t->bbox.y1-P2D(SELECT_PIXEL_LOOSENESS,v),
601 								 t->bbox.x2+P2D(SELECT_PIXEL_LOOSENESS,v),
602 								 t->bbox.y2+P2D(SELECT_PIXEL_LOOSENESS,v)))
603 			{
604 			match = add_to_list(match,0,0,(void *)t);
605 			return match;
606 			}
607 		else
608 			return match;
609 		}
610 	else
611 		{
612 		if (is_in_bbox(x,y,t->left->bbox.x1-P2D(SELECT_PIXEL_LOOSENESS,v),
613 								 t->left->bbox.y1-P2D(SELECT_PIXEL_LOOSENESS,v),
614 								 t->left->bbox.x2+P2D(SELECT_PIXEL_LOOSENESS,v),
615 								 t->left->bbox.y2+P2D(SELECT_PIXEL_LOOSENESS,v)))
616 			match = object_at_point0(match, v, t->left,x,y);
617 
618 		if (is_in_bbox(x,y,t->right->bbox.x1-P2D(SELECT_PIXEL_LOOSENESS,v),
619 								 t->right->bbox.y1-P2D(SELECT_PIXEL_LOOSENESS,v),
620 								 t->right->bbox.x2+P2D(SELECT_PIXEL_LOOSENESS,v),
621 								 t->right->bbox.y2+P2D(SELECT_PIXEL_LOOSENESS,v)))
622 			match = object_at_point0(match, v, t->right,x,y);
623 
624 			return match;
625 
626 		};
627 }
628 
629 /* return object at pixel coords x,y  */
630 Node *
object_at_point_p(View * v,Tree t,long x,long y)631 object_at_point_p(View *v, Tree t, long x, long y)
632 {
633 	return object_at_point(v, t, XP2D(x,v), YP2D(y,v));
634 }
635 
636 /* area coherence test: should we change object selection ?  */
637 Boolean
still_same_object(Node * n,long x,long y)638 still_same_object(Node *n, long x, long y)
639 {
640 	if (n==NULL)
641 		return FALSE;
642 
643 	if (!is_in_bbox(x,y,n->bbox.x1,n->bbox.y1,n->bbox.x2,n->bbox.y2))
644 		return FALSE;
645 
646 	if (n->up==NULL) /* only one object in tree !  */
647 		return TRUE;
648 
649 	if (n->up->right!=n && n->up->right!=NULL && is_in_bbox(x,y,n->up->right->bbox.x1,n->up->right->bbox.y1,n->up->right->bbox.x2,n->up->right->bbox.y2))
650 		return FALSE;
651 
652 	if (n->up->left!=n && n->up->left!=NULL && is_in_bbox(x,y,n->up->left->bbox.x1,n->up->left->bbox.y1,n->up->left->bbox.x2,n->up->left->bbox.y2))
653 		return FALSE;
654 
655 	return TRUE;
656 }
657 
658 Boolean
still_same_object_p(View * v,Node * n,long x,long y)659 still_same_object_p(View *v, Node *n, long x, long y)
660 {
661 	return still_same_object(n,XP2D(x,v),YP2D(y,v));
662 }
663 
664 /* return depth-sorted list of objects that intersect bb  */
665 List
intersecting_objects(Tree t,VRegion bb,List l)666 intersecting_objects(Tree t, VRegion bb, List l)
667 {
668 	List l2;
669 
670 	if (t==NULL)
671 		return l;
672 	else if (t->left==NULL && t->right==NULL) /* leaf node */
673 		{
674  		if (intersects_v(bb,t->bbox))
675 			{
676 			/* why like this ? for FIG_DEBUG_LIST_ALLOC */
677 			l2 = add_to_list(l,ULONG_MAX-t->ob->depth,0,(void *)t->ob);
678 			return l2;
679 			}
680  		else
681  			return l;
682 		}
683 	else
684 		{
685 		if (t->left!=NULL && intersects_v(bb,t->left->bbox))
686 			l = intersecting_objects(t->left,bb,l);
687 
688 		if (t->right!=NULL && intersects_v(bb,t->right->bbox))
689 			return intersecting_objects(t->right,bb,l);
690 
691 		return l;
692 		};
693 
694 }
695 
696 /* return depth-sorted list of objects that intersect cx,cy  */
697 List
intersecting_objects_point(Tree t,long cx,long cy,List l)698 intersecting_objects_point(Tree t, long cx, long cy, List l)
699 {
700 	List l2;
701 
702 	if (t==NULL)
703 		return l;
704 	else if (t->left==NULL && t->right==NULL) /* leaf node */
705 		{
706  		if (is_in_object(cx,cy,t->ob))
707 			{
708 			/* why like this ? for FIG_DEBUG_LIST_ALLOC */
709 			l2 = add_to_list(l,0,0,(void *)t->ob);
710 			return l2;
711 			}
712  		else
713  			return l;
714 		}
715 	else
716 		{
717 		if (t->left!=NULL && is_in_bbox(cx,cy,t->left->bbox.x1,t->left->bbox.y1,t->left->bbox.x2,t->left->bbox.y2))
718 			l = intersecting_objects_point(t->left,cx,cy,l);
719 
720 		if (t->right!=NULL && is_in_bbox(cx,cy,t->right->bbox.x1,t->right->bbox.y1,t->right->bbox.x2,t->right->bbox.y2))
721 			return intersecting_objects_point(t->right,cx,cy,l);
722 
723 		return l;
724 		};
725 }
726 
727 /* free all mem held by ob */
728 void
kill_object(Object * ob)729 kill_object(Object *ob)
730 {
731 	List l,l2;
732 
733 	switch (ob->type)
734 		{
735 		case TEXT:
736 			if (ob->ob.text.node && ob->ob.text.ellipse!=NULL)
737 				free(ob->ob.text.ellipse);
738 
739 			l = ob->ob.text.lines;
740 
741 			while (l!=NULL)
742 				{
743 				l2 = TEXTLINE(l)->sections;
744 				while (l2!=NULL)
745 					{
746 					free(TEXTSEC(l2));
747 					l2 = l2->next;
748 					};
749 				delete_list(TEXTLINE(l)->sections);
750 				free(TEXTLINE(l));
751 				l = l->next;
752 				};
753 			delete_list(ob->ob.text.lines);
754 			l=ob->derries;
755 			while (l!=NULL)
756 				{
757 				free(DERRY(l));
758 				l=l->next;
759 				};
760 			delete_list(ob->derries);
761 			free(ob);
762 			break;
763 
764 		case POLYLINE:
765 		case POLYGON:
766 			l = ob->ob.polyline.points;
767 
768 			while (l!=NULL)
769 				{
770 				free (POINT(l));
771 				l = l->next;
772 				};
773 
774 			if (ob->ob.polyline.pic)
775 				free (ob->ob.polyline.pic);
776 
777                         if (ob->farrow)
778 				free(ob->farrow);
779                         if (ob->barrow)
780 				free(ob->barrow);
781 
782 			delete_list(ob->ob.polyline.points);
783 			l=ob->derries;
784 			while (l!=NULL)
785 				{
786 				free(DERRY(l));
787 				l=l->next;
788 				};
789 			delete_list(ob->derries);
790 			free(ob);
791 			break;
792 
793 		case SPLINE:
794 		case ARC:
795 		 	l = ob->ob.spline.points;
796 			while (l!=NULL)
797 				{
798 				free(SPOINT(l));
799 				l = l->next;
800 				};
801 
802 			delete_list(ob->ob.spline.points);
803 
804 		 	l = ob->ob.spline.cache;
805 			while (l!=NULL)
806 				{
807 				free(POINT(l));
808 				l = l->next;
809 				};
810 
811                         if (ob->farrow)
812 				free(ob->farrow);
813                         if (ob->barrow)
814 				free(ob->barrow);
815 
816 			l=ob->derries;
817 			while (l!=NULL)
818 				{
819 				free(DERRY(l));
820 				l=l->next;
821 				};
822 			delete_list(ob->derries);
823 			delete_list(ob->ob.spline.cache);
824 			free(ob);
825 			break;
826 
827 		case ARCELLIPSE:
828                         if (ob->farrow)
829 				free(ob->farrow);
830                         if (ob->barrow)
831 				free(ob->barrow);
832 			l=ob->derries;
833 			while (l!=NULL)
834 				{
835 				free(DERRY(l));
836 				l=l->next;
837 				};
838 			delete_list(ob->derries);
839 			free(ob);
840 			break;
841 
842 		case ELLIPSE:
843 		case ROUNDBOX:
844 			l=ob->derries;
845 			while (l!=NULL)
846 				{
847 				free(DERRY(l));
848 				l=l->next;
849 				};
850 			delete_list(ob->derries);
851 			free(ob);
852 			break;
853 
854 		case COMPOUND:
855 
856 			l = ob->ob.compound.obs;
857 			while (l!=NULL)
858 				{
859 				kill_object(OB(l));
860 				l = l->next;
861 				};
862 			delete_list(ob->ob.compound.obs);
863 			l=ob->derries;
864 			while (l!=NULL)
865 				{
866 				free(DERRY(l));
867 				l=l->next;
868 				};
869 			delete_list(ob->derries);
870 			free(ob);
871 			break;
872 
873 		default:
874 			v_error(ob->type);
875 			break;
876 		};
877 }
878 
879 /* duplicate an object. the ticket value is altered  */
880 Object *
duplicate_object(Object * ob)881 duplicate_object(Object *ob)
882 {
883 	List l,l2;
884 	Object *nob;
885 	TextLine *tl;
886 	TextSection *ts;
887 	VPoint *p;
888 	SPoint *sp;
889 	uint c=0;
890 	uint c2=0;
891 
892 	nob = (Object *)malloc(sizeof(Object));
893 
894 	nob->type = ob->type;
895 	nob->ticket = ob_ticket++;
896 	nob->derries = NULL;
897 	nob->depth = ob->depth;
898 	nob->colour = ob->colour;
899 	nob->fillcolour = ob->fillcolour;
900 	nob->ls = ob->ls;
901 	nob->lw = ob->lw;
902 	nob->es = ob->es;
903 	nob->js = ob->js;
904 	nob->fs = ob->fs;
905 	nob->dash = ob->dash;
906 	nob->gap = ob->gap;
907 	nob->bbox = ob->bbox;
908 	if (ob->farrow!=NULL)
909 		nob->farrow = make_arrow(*ob->farrow);
910 	else
911 		nob->farrow = NULL;
912 	if (ob->barrow!=NULL)
913 		nob->barrow = make_arrow(*ob->barrow);
914 	else
915 		nob->barrow = NULL;
916 
917 	switch (ob->type)
918 		{
919 		case TEXT:
920 			if (ob->ob.text.node && ob->ob.text.ellipse!=NULL)
921 				nob->ob.text.ellipse = (void *)duplicate_object(ob->ob.text.ellipse);
922 
923 			nob->ob.text.node = ob->ob.text.node;
924 			nob->ob.text.bbox = ob->ob.text.bbox;
925 			nob->ob.text.angle = ob->ob.text.angle;
926 			nob->ob.text.lines = NULL;
927 			l = ob->ob.text.lines;
928 
929 			while (l!=NULL)
930 				{
931 				tl = (TextLine *)malloc(sizeof(TextLine));
932 				tl->y = TEXTLINE(l)->y;
933 				tl->h = TEXTLINE(l)->h;
934 				tl->just = TEXTLINE(l)->just;
935 				tl->sections = NULL;
936 				l2 = TEXTLINE(l)->sections;
937 				while (l2!=NULL)
938 					{
939 					ts = (TextSection *)malloc(sizeof(TextSection));
940 
941 					ts->num = TEXTSEC(l2)->num;
942 					ts->size = TEXTSEC(l2)->size;
943 					strcpy(ts->text,TEXTSEC(l2)->text);
944 					ts->x = TEXTSEC(l2)->x;
945 					ts->y = TEXTSEC(l2)->y;
946 					ts->w = TEXTSEC(l2)->w;
947 					ts->h = TEXTSEC(l2)->h;
948 					tl->sections = add_to_list(tl->sections, c,0,(void *)ts);
949 					c++;
950 
951 					l2 = l2->next;
952 					};
953 				nob->ob.text.lines = add_to_list(nob->ob.text.lines,c2,0, (void *)tl);
954 				c2++;
955 				l = l->next;
956 				};
957 			break;
958 
959 		case POLYLINE:
960 		case POLYGON:
961 			l = ob->ob.polyline.points;
962 
963 			if (ob->ob.polyline.pic)
964 				{
965 				nob->ob.polyline.pic = malloc(strlen(ob->ob.polyline.pic)+1);
966 				strcpy(nob->ob.polyline.pic, ob->ob.polyline.pic);
967 				}
968 			else
969 				nob->ob.polyline.pic = NULL;
970 
971 			nob->ob.polyline.points = NULL;
972 
973 			while (l!=NULL)
974 				{
975 				p = (VPoint *)malloc(sizeof(VPoint));
976 
977 				p->x = POINT(l)->x;
978 				p->y = POINT(l)->y;
979 				p->derried = FALSE;
980 				nob->ob.polyline.points = add_to_list(nob->ob.polyline.points,c,0,(void *)p);
981 				c++;
982 
983 				l = l->next;
984 				};
985 			break;
986 
987 		case ROUNDBOX:
988 			nob->ob.roundbox.radius = ob->ob.roundbox.radius;
989 			break;
990 
991 		case SPLINE:
992 		case ARC:
993 			nob->ob.spline.closed = ob->ob.spline.closed;
994 			nob->ob.spline.cache = NULL;
995 			nob->ob.spline.points = NULL;
996 
997 		 	l = ob->ob.spline.points;
998 			while (l!=NULL)
999 				{
1000 				sp = (SPoint *)malloc(sizeof(SPoint));
1001 
1002 				sp->x = SPOINT(l)->x;
1003 				sp->y = SPOINT(l)->y;
1004 				sp->derried = FALSE;
1005 				sp->s = SPOINT(l)->s;
1006 				nob->ob.spline.points = add_to_list(nob->ob.spline.points,c,0,(void *)sp);
1007 				c++;
1008 				l = l->next;
1009 				};
1010 				break;
1011 
1012 		case ELLIPSE:
1013 			nob->ob.ellipse.centre.x = ob->ob.ellipse.centre.x;
1014 			nob->ob.ellipse.centre.y = ob->ob.ellipse.centre.y;
1015 			nob->ob.ellipse.xradius = ob->ob.ellipse.xradius;
1016 			nob->ob.ellipse.yradius = ob->ob.ellipse.yradius;
1017 			break;
1018 
1019 		case ARCELLIPSE:
1020 			nob->ob.arcellipse.centre.x = ob->ob.arcellipse.centre.x;
1021 			nob->ob.arcellipse.centre.y = ob->ob.arcellipse.centre.y;
1022 			nob->ob.arcellipse.xradius = ob->ob.arcellipse.xradius;
1023 			nob->ob.arcellipse.yradius = ob->ob.arcellipse.yradius;
1024 			nob->ob.arcellipse.start = ob->ob.arcellipse.start;
1025 			nob->ob.arcellipse.end = ob->ob.arcellipse.end;
1026 			nob->ob.arcellipse.open = ob->ob.arcellipse.open;
1027 			break;
1028 
1029 		case COMPOUND:
1030 			nob->ob.compound.obs = NULL;
1031 
1032 			l = ob->ob.compound.obs;
1033 			while (l!=NULL)
1034 				{
1035 				Object *tob;
1036 				tob = duplicate_object(OB(l));
1037 				nob->ob.compound.obs = add_to_list(nob->ob.compound.obs,ULONG_MAX-OB(l)->depth,0,(void *)tob);
1038 				l = l->next;
1039 				};
1040 			break;
1041 
1042 		default:
1043 			v_error(ob->type);
1044 			break;
1045 		};
1046 
1047 	return nob;
1048 }
1049 
1050 /* move an object by document offset dx,dy  */
1051 void
move_object(View * view,Object * ob,long dx,long dy)1052 move_object(View *view, Object *ob, long dx, long dy)
1053 {
1054 	List l;
1055 
1056 	if (view->gridon)
1057 		{
1058 		dx = snap(view->grid_x,ob->bbox.x1+dx) - ob->bbox.x1;
1059 		dy = snap(view->grid_y,ob->bbox.y1+dy) - ob->bbox.y1;
1060 		};
1061 
1062 	ob->bbox.x1 += dx;
1063 	ob->bbox.y1 += dy;
1064 	ob->bbox.x2 += dx;
1065 	ob->bbox.y2 += dy;
1066 
1067 	if (ob->type==COMPOUND)
1068 		compound_move(view,ob,ob->bbox.x1-dx,ob->bbox.y1-dy,ob->bbox.x1,ob->bbox.y1);
1069 	else if (ob->type==TEXT)
1070 		{
1071 		ob->ob.text.bbox.x1 += dx;
1072 		ob->ob.text.bbox.y1 += dy;
1073 		ob->ob.text.bbox.x2 += dx;
1074 		ob->ob.text.bbox.y2 += dy;
1075 		};
1076 
1077 	if (ob->type==POLYLINE || ob->type==POLYGON)
1078 		{
1079 		l=ob->ob.polyline.points;
1080 		while (l!=NULL)
1081 			{
1082 			if (POINT(l)->derried)
1083 				{
1084 				/* derries to somewhere, so should stay where it is  */
1085 				POINT(l)->x -= dx;
1086 				POINT(l)->y -= dy;
1087 				recalc_polyline_bbox(ob, FALSE);
1088 				view->doc->o = change_bbox(view->doc->o, ob);
1089 				};
1090 			l=l->next;
1091 			};
1092 		}
1093 	else if (ob->type==SPLINE || ob->type==ARC)
1094 		{
1095 		l=ob->ob.spline.points;
1096 		while (l!=NULL)
1097 			{
1098 			if (SPOINT(l)->derried)
1099 				{
1100 				/* derries to somewhere, so should stay where it is  */
1101 				SPOINT(l)->x -= dx;
1102 				SPOINT(l)->y -= dy;
1103 				delete_cache_list(ob->ob.spline.cache);
1104 				ob->ob.spline.cache=NULL;
1105 				ob->ob.spline.cache = compute_spline_segment(ob->ob.spline.points,ob->ob.spline.closed);
1106 				recalc_polyline_bbox(ob, TRUE);
1107 				view->doc->o = change_bbox(view->doc->o, ob);
1108 				};
1109 			l=l->next;
1110 			};
1111 		};
1112 
1113 	view->doc->o = change_bbox(view->doc->o, ob);
1114 	view->selected_object = get_object_node(view->doc->o,ob);
1115 	view->highlighted_object = view->selected_object;
1116 	switch_icons(view);
1117 
1118 	/* if derries on object, they need to be moved  */
1119 	if (ob->type==TEXT && ob->ob.text.node && ob->ob.text.ellipse->derries!=NULL)
1120 		derry_move(view,ob->ob.text.ellipse,ob->ob.text.ellipse->bbox.x1-dx,ob->ob.text.ellipse->bbox.y1-dy);
1121 	else if (ob->derries!=NULL)
1122 		derry_move(view,ob,ob->bbox.x1-dx,ob->bbox.y1-dy);
1123 }
1124 
1125 /* flip object ob horizontally  */
1126 void
flip_object_x(View * view,Object * ob)1127 flip_object_x(View *view, Object *ob)
1128 {
1129 	flip_object_x0(view,ob,TRUE);
1130 }
1131 
1132 void
flip_object_x0(View * view,Object * ob,Boolean needs_undo)1133 flip_object_x0(View *view, Object *ob,Boolean needs_undo)
1134 {
1135 	List l;
1136 	long dx;
1137 	long tx;
1138 	double t;
1139 
1140 	if (needs_undo)
1141 		{
1142 		register_undo(UNDO_OB_PROP, ob, view->doc);
1143 		store_redraw_object(ob);
1144 		};
1145 
1146 	dx = ob->bbox.x2-ob->bbox.x1;
1147 
1148 	switch(ob->type)
1149 		{
1150 		case POLYLINE:
1151 		case POLYGON:
1152 			l = ob->ob.polyline.points;
1153 			while (l!=NULL)
1154 				{
1155 				POINT(l)->x = dx - POINT(l)->x;
1156 				l = l->next;
1157 				};
1158 			break;
1159 
1160 		case SPLINE:
1161 		case ARC:
1162 			l = ob->ob.spline.points;
1163 			while (l!=NULL)
1164 				{
1165 				SPOINT(l)->x = dx - SPOINT(l)->x;
1166 				l = l->next;
1167 				};
1168 			l = ob->ob.spline.cache;
1169 			while (l!=NULL)
1170 				{
1171 				POINT(l)->x = dx - POINT(l)->x;
1172 				l = l->next;
1173 				};
1174 			break;
1175 
1176 		case ARCELLIPSE:
1177 			if (ob->ob.arcellipse.start>ob->ob.arcellipse.end)
1178 				{
1179 				t = ob->ob.arcellipse.start;
1180 				ob->ob.arcellipse.start = ob->ob.arcellipse.end;
1181 				ob->ob.arcellipse.end = t;
1182 				};
1183 			ob->ob.arcellipse.start = ((180*64.0)/PI) * flip_angle_x((ob->ob.arcellipse.start*PI)/(64.0*180));
1184 			ob->ob.arcellipse.end = ((180*64.0)/PI) * flip_angle_x((ob->ob.arcellipse.end*PI)/(64.0*180));
1185 			t = ob->ob.arcellipse.start;
1186 			ob->ob.arcellipse.start = ob->ob.arcellipse.end;
1187 			ob->ob.arcellipse.end = t;
1188 			ob->ob.arcellipse.end += 360*64;
1189 			break;
1190 
1191 		case COMPOUND:
1192 			l = ob->ob.compound.obs;
1193 			while (l!=NULL)
1194 				{
1195 				OB(l)->bbox.x1 -= ob->bbox.x1;
1196 				tx = dx - OB(l)->bbox.x1;
1197 				OB(l)->bbox.x2 -= ob->bbox.x1;
1198 				OB(l)->bbox.x1 = dx - OB(l)->bbox.x2;
1199 				OB(l)->bbox.x2 = tx;
1200 				OB(l)->bbox.x1 += ob->bbox.x1;
1201 				OB(l)->bbox.x2 += ob->bbox.x1;
1202 				flip_object_x0(view,OB(l),FALSE);
1203 				l = l->next;
1204 				};
1205 			break;
1206 
1207 		default:
1208 			/* not handled */
1209 			break;
1210 		};
1211 
1212 	if (needs_undo)
1213 		send_stored_redraw_object(view, ob);
1214 }
1215 
1216 /* flip object ob vertically  */
1217 void
flip_object_y(View * view,Object * ob)1218 flip_object_y(View *view, Object *ob)
1219 {
1220 	flip_object_y0(view,ob,TRUE);
1221 }
1222 
1223 void
flip_object_y0(View * view,Object * ob,Boolean needs_undo)1224 flip_object_y0(View *view, Object *ob,Boolean needs_undo)
1225 {
1226 	List l;
1227 	long dy;
1228 	long ty;
1229 	double t;
1230 
1231 	if (needs_undo)
1232 		{
1233 		register_undo(UNDO_OB_PROP, ob, view->doc);
1234 		store_redraw_object(ob);
1235 		};
1236 
1237 	dy = ob->bbox.y2-ob->bbox.y1;
1238 
1239 	switch(ob->type)
1240 		{
1241 		case POLYLINE:
1242 		case POLYGON:
1243 			l = ob->ob.polyline.points;
1244 			while (l!=NULL)
1245 				{
1246 				POINT(l)->y = dy - POINT(l)->y;
1247 				l = l->next;
1248 				};
1249 			break;
1250 
1251 		case SPLINE:
1252 		case ARC:
1253 			l = ob->ob.spline.points;
1254 			while (l!=NULL)
1255 				{
1256 				SPOINT(l)->y = dy - SPOINT(l)->y;
1257 				l = l->next;
1258 				};
1259 			l = ob->ob.spline.cache;
1260 			while (l!=NULL)
1261 				{
1262 				POINT(l)->y = dy - POINT(l)->y;
1263 				l = l->next;
1264 				};
1265 			break;
1266 
1267 		case ARCELLIPSE:
1268 			ob->ob.arcellipse.start = ((180*64.0)/PI) * flip_angle_y((ob->ob.arcellipse.start*PI)/(64.0*180));
1269 			ob->ob.arcellipse.end = ((180*64.0)/PI) * flip_angle_y((ob->ob.arcellipse.end*PI)/(64.0*180));
1270 			t = ob->ob.arcellipse.start;
1271 			ob->ob.arcellipse.start = ob->ob.arcellipse.end;
1272 			ob->ob.arcellipse.end = t;
1273 			ob->ob.arcellipse.end += 360*64;
1274 			break;
1275 
1276 		case COMPOUND:
1277 			l = ob->ob.compound.obs;
1278 			while (l!=NULL)
1279 				{
1280 				OB(l)->bbox.y1 -= ob->bbox.y1;
1281 				ty = dy - OB(l)->bbox.y1;
1282 				OB(l)->bbox.y2 -= ob->bbox.y1;
1283 				OB(l)->bbox.y1 = dy - OB(l)->bbox.y2;
1284 				OB(l)->bbox.y2 = ty;
1285 				OB(l)->bbox.y1 += ob->bbox.y1;
1286 				OB(l)->bbox.y2 += ob->bbox.y1;
1287 				flip_object_y0(view,OB(l),FALSE);
1288 				l = l->next;
1289 				};
1290 			break;
1291 
1292 		default:
1293 			/* not handled */
1294 			break;
1295 		};
1296 
1297 	if (needs_undo)
1298 		send_stored_redraw_object(view, ob);
1299 }
1300 
1301 /* rotate an object by angle  */
1302 void
apply_rotate_to_object(View * view,Object * ob,double angle)1303 apply_rotate_to_object(View *view, Object *ob, double angle)
1304 {
1305 	double cosa, sina;
1306 	long cx,cy;
1307 	List l;
1308 	long x,y;
1309 	Object *s;
1310 
1311 
1312 	register_undo(UNDO_OB_PROP, ob, view->doc);
1313 
1314 	cosa = cosround(angle);
1315 	sina = sinround(angle);
1316 	cx = (ob->bbox.x2-ob->bbox.x1)/2;
1317 	cy = (ob->bbox.y2-ob->bbox.y1)/2;
1318 
1319 	SET_CLIP_WINDOW(handlegc, view->draw_window);
1320 
1321  	store_redraw_object(ob);
1322 
1323 	switch(ob->type)
1324 		{
1325 		case TEXT:
1326 			ob->ob.text.angle = ob->ob.text.angle+angle;
1327 			/* if it's a small rotation, default to 0.0  */
1328 			if (abs(ob->ob.text.angle)<((3*PI)/180))
1329 				ob->ob.text.angle=0.0;
1330 
1331 			calc_text_outer_box_adjust(ob,0,0);
1332 
1333 			s = view->selected_object->ob;
1334   			view->doc->o = change_bbox(view->doc->o, ob);
1335 			view->selected_object = get_object_node(view->doc->o, s);
1336 			view->highlighted_object = view->selected_object;
1337 			break;
1338 
1339 		case POLYLINE:
1340 		case POLYGON:
1341 			l = ob->ob.polyline.points;
1342 
1343 			while (l!=NULL)
1344 				{
1345 				x = cx+(cosa*(POINT(l)->x-cx))-(sina*(POINT(l)->y-cy));
1346 				y = cy+(sina*(POINT(l)->x-cx))+(cosa*(POINT(l)->y-cy));
1347 				POINT(l)->x = x;
1348 				POINT(l)->y = y;
1349 				/* remove if derry  */
1350 				if (POINT(l)->derried)
1351 					remove_derry(view,ob,POINT(l));
1352 				l=l->next;
1353 				};
1354 
1355   			recalc_polyline_bbox(ob, FALSE);
1356 			s = view->selected_object->ob;
1357   			view->doc->o = change_bbox(view->doc->o, ob);
1358 			view->selected_object = get_object_node(view->doc->o, s);
1359 			view->highlighted_object = view->selected_object;
1360 			break;
1361 
1362 		case SPLINE:
1363 		case ARC:
1364 			l = ob->ob.spline.points;
1365 
1366 			while (l!=NULL)
1367 				{
1368 				x = cx+(cosa*(SPOINT(l)->x-cx))-(sina*(SPOINT(l)->y-cy));
1369 				y = cy+(sina*(SPOINT(l)->x-cx))+(cosa*(SPOINT(l)->y-cy));
1370 				SPOINT(l)->x = x;
1371 				SPOINT(l)->y = y;
1372 				/* remove if derry  */
1373 				if (POINT(l)->derried)
1374 					remove_derry(view,ob,POINT(l));
1375 				l=l->next;
1376 				};
1377 
1378 			if (!ob->ob.spline.cache)
1379 				ob->ob.spline.cache = compute_spline_segment(ob->ob.spline.points, ob->ob.spline.closed);
1380 
1381 			l = ob->ob.spline.cache;
1382 
1383 			while (l!=NULL)
1384 				{
1385 				x = cx+(cosa*(POINT(l)->x-cx))-(sina*(POINT(l)->y-cy));
1386 				y = cy+(sina*(POINT(l)->x-cx))+(cosa*(POINT(l)->y-cy));
1387 				POINT(l)->x = x;
1388 				POINT(l)->y = y;
1389 				l=l->next;
1390 				};
1391 
1392   			recalc_polyline_bbox(ob, TRUE);
1393 			s = view->selected_object->ob;
1394   			view->doc->o = change_bbox(view->doc->o, ob);
1395 			view->selected_object = get_object_node(view->doc->o, s);
1396 			view->highlighted_object = view->selected_object;
1397 			break;
1398 
1399 		default:
1400 			/* object type not handled */
1401 			break;
1402 		};
1403 
1404   	send_stored_redraw_object(view, ob);
1405 }
1406 
1407 /* scale a compound object  */
1408 /* must be treated differently to pass a centre of rotation  */
1409 void
apply_scale_to_compound(View * view,Object * ob,double x,double y,long x1,long y1)1410 apply_scale_to_compound(View *view, Object *ob, double x, double y, long x1, long y1)
1411 {
1412 	List l;
1413 	Object *s;
1414 
1415 	l = ob->ob.compound.obs;
1416 
1417 	ob->bbox.x2 = ob->bbox.x1+1;
1418 	ob->bbox.y2 = ob->bbox.y1+1;
1419 
1420 	while (l!=NULL)
1421 		{
1422 		if (OB(l)->type==TEXT)
1423 			{
1424 			OB(l)->ob.text.bbox.x1 = x1 + R(OB(l)->ob.text.bbox.x1-x1,x);
1425 			OB(l)->ob.text.bbox.y1 = y1 + R(OB(l)->ob.text.bbox.y1-y1,y);
1426 			OB(l)->ob.text.bbox.x2 = x1 + R(OB(l)->ob.text.bbox.x2-x1,x);
1427 			OB(l)->ob.text.bbox.y2 = y1 + R(OB(l)->ob.text.bbox.y2-y1,y);
1428 			}
1429 		else
1430 			{
1431 			OB(l)->bbox.x1 = x1 + R(OB(l)->bbox.x1-x1,x);
1432 			OB(l)->bbox.y1 = y1 + R(OB(l)->bbox.y1-y1,y);
1433 			OB(l)->bbox.x2 = x1 + R(OB(l)->bbox.x2-x1,x);
1434 			OB(l)->bbox.y2 = y1 + R(OB(l)->bbox.y2-y1,y);
1435 			};
1436 		if (OB(l)->type==COMPOUND)
1437 			apply_scale_to_compound(view,OB(l),x,y,x1,y1);
1438 		else
1439 			apply_scale_to_object(view,OB(l),x,y,FALSE);
1440 		ob->bbox = merge_boxes(ob->bbox,OB(l)->bbox);
1441 		view->doc->o = trash_object(view->doc->o,&view->doc->lo,OB(l));
1442 		l = l->next;
1443 		};
1444 
1445 	s = view->selected_object->ob;
1446   	view->doc->o = change_bbox(view->doc->o, ob);
1447 	view->selected_object = get_object_node(view->doc->o, s);
1448 	view->highlighted_object = view->selected_object;
1449 }
1450 
1451 /* scale object ob by ratios x and y  */
1452 void
apply_scale_to_object(View * view,Object * ob,double x,double y,Boolean part_of_compound)1453 apply_scale_to_object(View *view, Object *ob, double x, double y, Boolean part_of_compound)
1454 {
1455 	List l,l2;
1456 	Object *s;
1457 	long ox=0,oy=0;
1458 
1459 	x = abs(x);
1460 	y = abs(y);
1461 
1462 	if (part_of_compound)
1463 		{
1464 		register_undo(UNDO_OB_PROP, ob, view->doc);
1465 		/* ugh !  */
1466 		if (ob->type==TEXT)
1467 			{
1468 			VRegion bb;
1469 			bb.x1 = ob->bbox.x1; bb.y1 = ob->bbox.y1;
1470 			bb.x2 = ob->bbox.x1 + R(ob->bbox.x2-ob->bbox.x1,x);
1471 			bb.y2 = ob->bbox.y1 + R(ob->bbox.y2-ob->bbox.y1,y);
1472 			ob->bbox = merge_boxes(ob->bbox,bb);
1473 			};
1474 		store_redraw_object(ob);
1475 		};
1476 
1477 	corner_magic(ob, &ob->bbox.x1, &ob->bbox.y1, x, y);
1478 	switch(ob->type)
1479 		{
1480 		case TEXT:
1481 			if (ob->ob.text.angle!=0.0)
1482 				{
1483 				double sina,cosa;
1484 				long cx,cy;
1485 				sina = sinround(ob->ob.text.angle);
1486 				cosa = cosround(ob->ob.text.angle);
1487 				cx = (ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)/2 + ob->ob.text.bbox.x1;
1488 				cy = (ob->ob.text.bbox.y2-ob->ob.text.bbox.y1)/2 + ob->ob.text.bbox.y1;
1489 				ox = cx+(cosa*(ob->ob.text.bbox.x1-cx))-(sina*(ob->ob.text.bbox.y1-cy));
1490 				oy = cy+(sina*(ob->ob.text.bbox.x1-cx))+(cosa*(ob->ob.text.bbox.y1-cy));
1491 				};
1492 
1493 			corner_magic(ob, &ob->ob.text.bbox.x1, &ob->ob.text.bbox.y1, x, y);
1494 			(x<y) ? (y=x) : (x=y);
1495 			if (part_of_compound)
1496 				{
1497 				ob->ob.text.bbox.x2 = R(ob->ob.text.bbox.x2-ob->ob.text.bbox.x1,x);
1498 				ob->ob.text.bbox.x2 += ob->ob.text.bbox.x1;
1499 				ob->ob.text.bbox.y2 = R(ob->ob.text.bbox.y2-ob->bbox.y1,y);
1500 				ob->ob.text.bbox.y2 += ob->ob.text.bbox.y1;
1501 				};
1502 			l = ob->ob.text.lines;
1503 			while (l!=NULL)
1504 				{
1505 				l2 = TEXTLINE(l)->sections;
1506 				while (l2!=NULL)
1507 					{
1508 					TEXTSEC(l2)->size=y*TEXTSEC(l2)->size;
1509 					l2=l2->next;
1510 					};
1511 				l=l->next;
1512 				};
1513 			recalc_text_box(view,ob,FALSE,FALSE);
1514 			if (ob->ob.text.angle!=0.0)
1515 				{
1516 				double sina,cosa;
1517 				long cx,cy;
1518 				long nx,ny;
1519 				sina = sinround(ob->ob.text.angle);
1520 				cosa = cosround(ob->ob.text.angle);
1521 				cx = (ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)/2 + ob->ob.text.bbox.x1;
1522 				cy = (ob->ob.text.bbox.y2-ob->ob.text.bbox.y1)/2 + ob->ob.text.bbox.y1;
1523 				nx = cx+(cosa*(ob->ob.text.bbox.x1-cx))-(sina*(ob->ob.text.bbox.y1-cy));
1524 				ny = cy+(sina*(ob->ob.text.bbox.x1-cx))+(cosa*(ob->ob.text.bbox.y1-cy));
1525 				ob->bbox.x1 -= nx - ox;
1526 				ob->bbox.y1 -= ny - oy;
1527 				ob->bbox.x2 -= nx - ox;
1528 				ob->bbox.y2 -= ny - oy;
1529 				ob->ob.text.bbox.x1 -= nx - ox;
1530 				ob->ob.text.bbox.y1 -= ny - oy;
1531 				ob->ob.text.bbox.x2 -= nx - ox;
1532 				ob->ob.text.bbox.y2 -= ny - oy;
1533 				};
1534 			s = view->selected_object->ob;
1535   			view->doc->o = change_bbox(view->doc->o, ob);
1536 			view->selected_object = get_object_node(view->doc->o, s);
1537 			view->highlighted_object = view->selected_object;
1538 			break;
1539 
1540 		case ROUNDBOX:
1541 			if (part_of_compound)
1542 				{
1543 				ob->bbox.x2 = R(ob->bbox.x2-ob->bbox.x1,x);
1544 				ob->bbox.x2 += ob->bbox.x1;
1545 				ob->bbox.y2 = R(ob->bbox.y2-ob->bbox.y1,y);
1546 				ob->bbox.y2 += ob->bbox.y1;
1547 				};
1548 			s = view->selected_object->ob;
1549   			view->doc->o = change_bbox(view->doc->o, ob);
1550 			view->selected_object = get_object_node(view->doc->o, s);
1551 			view->highlighted_object = view->selected_object;
1552 			break;
1553 
1554 		case ELLIPSE:
1555 			ob->ob.ellipse.centre.x = R(ob->ob.ellipse.centre.x,x);
1556 			ob->ob.ellipse.centre.y = R(ob->ob.ellipse.centre.y,y);
1557 			ob->ob.ellipse.xradius = R(ob->ob.ellipse.xradius,x);
1558 			ob->ob.ellipse.yradius = R(ob->ob.ellipse.yradius,y);
1559 			ob->bbox.x2 = ob->bbox.x1 + ob->ob.ellipse.centre.x + ob->ob.ellipse.xradius;
1560 			ob->bbox.y2 = ob->bbox.y1 + ob->ob.ellipse.centre.y + ob->ob.ellipse.yradius;
1561 			s = view->selected_object->ob;
1562   			view->doc->o = change_bbox(view->doc->o, ob);
1563 			view->selected_object = get_object_node(view->doc->o, s);
1564 			view->highlighted_object = view->selected_object;
1565 			break;
1566 
1567 		case ARCELLIPSE:
1568 			/* must constrain to circular shape */
1569 			/* this works very badly in compounds, but there doesn't
1570 				seem to be a better way to ensure arc is contained */
1571 			(x<y) ? (y=x) : (x=y);
1572 			ob->ob.arcellipse.centre.x = R(ob->ob.arcellipse.centre.x,x);
1573 			ob->ob.arcellipse.centre.y = R(ob->ob.arcellipse.centre.y,y);
1574 			ob->ob.arcellipse.xradius = R(ob->ob.arcellipse.xradius,x);
1575 			ob->ob.arcellipse.yradius = R(ob->ob.arcellipse.yradius,y);
1576 			ob->bbox.x2 = ob->bbox.x1 + ob->ob.arcellipse.centre.x + ob->ob.arcellipse.xradius;
1577 			ob->bbox.y2 = ob->bbox.y1 + ob->ob.arcellipse.centre.y + ob->ob.arcellipse.yradius;
1578 			s = view->selected_object->ob;
1579   			view->doc->o = change_bbox(view->doc->o, ob);
1580 			view->selected_object = get_object_node(view->doc->o, s);
1581 			view->highlighted_object = view->selected_object;
1582 			break;
1583 
1584 		case POLYGON:
1585 		case POLYLINE:
1586 			l = ob->ob.polyline.points;
1587 
1588 			while (l!=NULL)
1589 				{
1590 				POINT(l)->x = R(POINT(l)->x,x);
1591 				POINT(l)->y = R(POINT(l)->y,y);
1592 				if (POINT(l)->derried)
1593 					remove_derry(view,ob,POINT(l));
1594 				l = l->next;
1595 				};
1596 
1597 			recalc_polyline_bbox(ob, FALSE);
1598 			s = view->selected_object->ob;
1599   			view->doc->o = change_bbox(view->doc->o, ob);
1600 			view->selected_object = get_object_node(view->doc->o, s);
1601 			view->highlighted_object = view->selected_object;
1602 			break;
1603 
1604 		case SPLINE:
1605 		case ARC:
1606 			l = ob->ob.spline.points;
1607 
1608 			while (l!=NULL)
1609 				{
1610 				SPOINT(l)->x = R(SPOINT(l)->x,x);
1611 				SPOINT(l)->y = R(SPOINT(l)->y,y);
1612 				if (POINT(l)->derried)
1613 					remove_derry(view,ob,POINT(l));
1614 				l = l->next;
1615 				};
1616 
1617 			delete_cache_list(ob->ob.spline.cache);
1618 			ob->ob.spline.cache = compute_spline_segment(ob->ob.spline.points,ob->ob.spline.closed);
1619 			recalc_polyline_bbox(ob, TRUE);
1620 			s = view->selected_object->ob;
1621   			view->doc->o = change_bbox(view->doc->o, ob);
1622 			view->selected_object = get_object_node(view->doc->o, s);
1623 			view->highlighted_object = view->selected_object;
1624 			break;
1625 
1626 		case COMPOUND:
1627 			apply_scale_to_compound(view,ob,x,y,ob->bbox.x1,ob->bbox.y1);
1628 			break;
1629 
1630 		default:
1631 			v_error(ob->type);
1632 			break;
1633 		};
1634 
1635 	if (part_of_compound)
1636 		send_stored_redraw_object(view, ob);
1637 }
1638 
1639 /* request for a derry. Search through objects intersecting this point
1640 	and pick out the contributed point that is closest. The caller then
1641 	sets up the connection with the returned value *rob */
1642 Boolean
get_nearest_point(View * view,Tree t,long cx,long cy,long * x,long * y,Object ** rob)1643 get_nearest_point(View *view, Tree t, long cx, long cy, long *x,long *y,Object **rob)
1644 {
1645 	List l=NULL;
1646 	List l2;
1647 	List points;
1648 	VRegion bb;
1649 	double rcx, rcy;
1650 	double tx,ty;
1651 	double ax,ay;
1652 	double bx,by;
1653 	long ix, iy;
1654 	Object *ob;
1655 	double theta;
1656 	double theta2;
1657 	Boolean found=FALSE;
1658 	double nearest=600000000.0; /* far away  */
1659 
1660 	if (t==NULL)
1661 		return FALSE;
1662 
1663 	/* look thirty pixels in every direction  */
1664 	bb.x1 = cx - P2D(30,view);
1665 	bb.y1 = cy - P2D(30,view);
1666 	bb.x2 = cx + P2D(30,view);
1667 	bb.y2 = cy + P2D(30,view);
1668 	l = intersecting_objects(t,bb,l);
1669 	l2=l;
1670 	while (l!=NULL)
1671 		{
1672 		ob = OB(l);
1673 
1674 		switch (ob->type)
1675 			{
1676 			case POLYLINE:
1677 			case POLYGON:
1678 				rcx = cx - ob->bbox.x1;
1679 				rcy = cy - ob->bbox.y1;
1680 
1681 				points = ob->ob.polyline.points;
1682 
1683 				while (points!=NULL)
1684 					{
1685 					if (sqrt((double)((POINT(points)->x-rcx)*(POINT(points)->x-rcx))+((POINT(points)->y-rcy)*(POINT(points)->y-rcy)))
1686 						 < nearest)
1687 						{
1688 						nearest = sqrt((double)((POINT(points)->x-rcx)*(POINT(points)->x-rcx))+((POINT(points)->y-rcy)*(POINT(points)->y-rcy)));
1689 						*x = ob->bbox.x1 + POINT(points)->x;
1690 						*y = ob->bbox.y1 + POINT(points)->y;
1691 						found = TRUE;
1692 						*rob = ob;
1693 						};
1694 					points = points->next;
1695 					};
1696 				break;
1697 
1698 			case SPLINE:
1699 			case ARC:
1700 				if (ob->ob.spline.closed)
1701 					break;
1702 
1703 				rcx = cx - ob->bbox.x1;
1704 				rcy = cy - ob->bbox.y1;
1705 
1706 				points = ob->ob.spline.points;
1707 
1708 				while (points!=NULL)
1709 					{
1710 					/* only jump if curve is near point */
1711 					/* ideally, this should take account of when curve passes through,
1712 						but has an approximated shape factor (three-point straight line) */
1713 					if (SPOINT(points)->s<SHAPE_ATTACH_THRESHOLD &&
1714 					sqrt((double)((SPOINT(points)->x-rcx)*(SPOINT(points)->x-rcx))+((SPOINT(points)->y-rcy)*(SPOINT(points)->y-rcy)))
1715 						 < nearest)
1716 						{
1717 						nearest = sqrt((double)((SPOINT(points)->x-rcx)*(SPOINT(points)->x-rcx))+((SPOINT(points)->y-rcy)*(SPOINT(points)->y-rcy)));
1718 						*x = ob->bbox.x1 + SPOINT(points)->x;
1719 						*y = ob->bbox.y1 + SPOINT(points)->y;
1720 						found = TRUE;
1721 						*rob = ob;
1722 						};
1723 					points = points->next;
1724 					};
1725 				break;
1726 
1727 			case TEXT:
1728 				if (!ob->ob.text.node)
1729 					break;
1730 				else
1731 					{
1732 					long xr,yr;
1733 					Object *eob=ob->ob.text.ellipse;
1734 
1735 					/* only attempt to derry to node's ellipse  */
1736 					xr = eob->ob.ellipse.xradius;
1737 					yr = eob->ob.ellipse.yradius;
1738 
1739 					theta = atan2((((double)yr)/xr)*(cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1740 								  (((double)yr)/xr)*(((cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1741 
1742 
1743 					tx = xr * cosround(theta);
1744 					ty = yr * sinround(theta);
1745 					ix = tx + ob->bbox.x1 + (ob->bbox.x2-ob->bbox.x1)/2;
1746 					iy = ty + ob->bbox.y1 + (ob->bbox.y2-ob->bbox.y1)/2;
1747 					if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy))) < nearest)
1748 						{
1749 						/* kludge factor  */
1750 						/* this is to attempt to determine whether we are on the ellipse line or not already  */
1751 						/* it's far from perfect */
1752 						theta2 = atan2((((double)yr)/xr)*(iy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1753 										  (((double)yr)/xr)*(((ix-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1754 						ax = (double)cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0);
1755 						ay = (double)cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0);
1756 						bx = (double)xr;
1757 						by = (double)yr;
1758 						if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)))*(50*abs(1-((ax*ax)/(bx*bx)+(ay*ay)/(by*by)))) > 500.0)
1759 							{
1760 							*x = ix;
1761 							*y = iy;
1762 							}
1763 						else
1764 							{
1765 							*x = cx;
1766 							*y = cy;
1767 							};
1768 						found = TRUE;
1769 						*rob = eob;
1770 						nearest = sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)));
1771 						};
1772 					};
1773 				break;
1774 
1775 			case ARCELLIPSE:
1776 				theta = atan2((((double)ob->ob.arcellipse.yradius)/ob->ob.ellipse.xradius)*(cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1777 								  (((double)ob->ob.arcellipse.yradius)/ob->ob.ellipse.xradius)*(((cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1778 
1779 				tx = ob->ob.arcellipse.xradius * cosround(theta);
1780 				ty = ob->ob.arcellipse.yradius * sinround(theta);
1781 				ix = tx + ob->bbox.x1 + (ob->bbox.x2-ob->bbox.x1)/2;
1782 				iy = ty + ob->bbox.y1 + (ob->bbox.y2-ob->bbox.y1)/2;
1783 				if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy))) < nearest)
1784 					{
1785 					/* kludge factor  */
1786 					/* this is to attempt to determine whether we are on the ellipse line or not already  */
1787 					/* it's far from perfect */
1788 					theta2 = atan2((((double)ob->ob.arcellipse.yradius)/ob->ob.arcellipse.xradius)*(iy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1789 									  (((double)ob->ob.arcellipse.yradius)/ob->ob.arcellipse.xradius)*(((ix-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1790 					ax = (double)cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0);
1791 					ay = (double)cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0);
1792 					bx = (double)ob->ob.arcellipse.xradius;
1793 					by = (double)ob->ob.arcellipse.yradius;
1794 					if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)))*(50*abs(1-((ax*ax)/(bx*bx)+(ay*ay)/(by*by)))) > 500.0)
1795 						{
1796 						*x = ix;
1797 						*y = iy;
1798 						}
1799 					else
1800 						{
1801 						*x = cx;
1802 						*y = cy;
1803 						};
1804 					*rob = ob;
1805 					found = TRUE;
1806 					nearest = sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)));
1807 					};
1808 				break;
1809 
1810 			case ELLIPSE:
1811 				theta = atan2((((double)ob->ob.ellipse.yradius)/ob->ob.ellipse.xradius)*(cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1812 								  (((double)ob->ob.ellipse.yradius)/ob->ob.ellipse.xradius)*(((cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1813 
1814 				tx = ob->ob.ellipse.xradius * cosround(theta);
1815 				ty = ob->ob.ellipse.yradius * sinround(theta);
1816 				ix = tx + ob->bbox.x1 + (ob->bbox.x2-ob->bbox.x1)/2;
1817 				iy = ty + ob->bbox.y1 + (ob->bbox.y2-ob->bbox.y1)/2;
1818 				if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy))) < nearest)
1819 					{
1820 					/* kludge factor  */
1821 					/* this is to attempt to determine whether we are on the ellipse line or not already  */
1822 					/* it's far from perfect */
1823 					theta2 = atan2((((double)ob->ob.ellipse.yradius)/ob->ob.ellipse.xradius)*(iy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0)),
1824 									  (((double)ob->ob.ellipse.yradius)/ob->ob.ellipse.xradius)*(((ix-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0)))));
1825 					ax = (double)cx-(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2.0);
1826 					ay = (double)cy-(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2.0);
1827 					bx = (double)ob->ob.ellipse.xradius;
1828 					by = (double)ob->ob.ellipse.yradius;
1829 					if (sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)))*(50*abs(1-((ax*ax)/(bx*bx)+(ay*ay)/(by*by)))) > 500.0)
1830 						{
1831 						*x = ix;
1832 						*y = iy;
1833 						}
1834 					else
1835 						{
1836 						*x = cx;
1837 						*y = cy;
1838 						};
1839 					*rob = ob;
1840 					found = TRUE;
1841 					nearest = sqrt((double)((cx-ix)*(cx-ix))+((cy-iy)*(cy-iy)));
1842 					};
1843 				break;
1844 
1845 			default:
1846 				/* not an error, just an uninteresting object */
1847 				break;
1848 			};
1849 
1850 		l = l->next;
1851 		};
1852 	delete_list(l2);
1853 	return found;
1854 }
1855 
1856 /* Adds a derry to the host object */
1857 /* the point is flagged as derried in the source object  */
1858 void
add_derry(Object * ob,Object * src,VPoint * point)1859 add_derry(Object *ob, Object *src, VPoint *point)
1860 {
1861 	Derry *d;
1862 
1863 	d = (Derry *)malloc(sizeof(Derry));
1864 
1865 	d->ticket = src->ticket;
1866 	d->point = point;
1867 
1868 	ob->derries = add_to_list(ob->derries,src->ticket,0,(void *)d);
1869 }
1870 
1871 /* to move all derryees  */
1872 void
derry_move(View * v,Object * ob,long oldx,long oldy)1873 derry_move(View *v,Object *ob,long oldx,long oldy)
1874 {
1875 	long x,y;
1876 	long dx,dy;
1877 	Object *nob=NULL;
1878 	List l;
1879 
1880 	dx = ob->bbox.x1-oldx;
1881 	dy = ob->bbox.y1-oldy;
1882 
1883 	l=ob->derries;
1884 
1885 	while (l!=NULL)
1886 		{
1887 		nob = get_object_by_ticket(v->doc->o,DERRY(l)->ticket);
1888 		if (nob!=NULL)
1889 			{
1890 			store_redraw_object(nob);
1891 			x = nob->bbox.x1 + DERRY(l)->point->x;
1892 			y = nob->bbox.y1 + DERRY(l)->point->y;
1893 
1894 			x += dx;
1895 			y += dy;
1896 			DERRY(l)->point->x = x - nob->bbox.x1;
1897 			DERRY(l)->point->y = y - nob->bbox.y1;
1898 			if (nob->type==SPLINE || nob->type==ARC)
1899 				{
1900 				if (nob->type==ARC)
1901 					{
1902 					SPoint *p1,*p2,*p3;
1903 					short a,b;
1904 					p1 = SPOINT(nob->ob.spline.points);
1905 					p2 = SPOINT(nob->ob.spline.points->next);
1906 					p3 = SPOINT(nob->ob.spline.points->next->next);
1907 					bent_midpoint(p1->x,	p1->y,p3->x,p3->y, 0.261799388, &a, &b);
1908 					p2->x = a;
1909 					p2->y = b;
1910 					};
1911 				delete_cache_list(nob->ob.spline.cache);
1912 				nob->ob.spline.cache = compute_spline_segment(nob->ob.spline.points,nob->ob.spline.closed);
1913 				};
1914 			recalc_polyline_bbox(nob,(nob->type==SPLINE || nob->type==ARC));
1915 			v->doc->o = change_bbox(v->doc->o, nob);
1916 			send_stored_redraw_object(v,nob);
1917 			};
1918 
1919 		l = l->next;
1920 		};
1921 }
1922 
1923 /* remove a no-longer-valid attachment  */
1924 void
remove_derry(View * v,Object * ob,VPoint * p)1925 remove_derry(View *v,Object *ob, VPoint *p)
1926 {
1927 	List l,l2;
1928 	Derry *d;
1929 	Object *nob=NULL;
1930 
1931 	l = v->doc->lo;
1932 
1933 	while (l!=NULL)
1934 		{
1935 		if (OB(l)->derries!=NULL)
1936 			{
1937 			l2 = OB(l)->derries;
1938 			while (l2!=NULL)
1939 				{
1940 				nob = get_object_by_ticket(v->doc->o,DERRY(l2)->ticket);
1941 				if (nob==ob && DERRY(l2)->point==p)
1942 					{
1943 					d = DERRY(l2);
1944 					OB(l)->derries = delete_from_list_v(OB(l)->derries,l2->data);
1945 					free(d);
1946 					l2 = OB(l)->derries;
1947 					}
1948 				else
1949 					l2 = l2->next;
1950 				};
1951 			};
1952 		l = l->next;
1953 		};
1954 	p->derried=FALSE;
1955 }
1956