1 /* Utility functions used  */
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 /*
18  * $Log: util.c,v $
19  * Revision 1.3  2000/12/17 00:57:43  moz
20  * examples, filled open splines, highlight_objects
21  *
22  * Revision 1.2  2000/12/06 20:56:07  moz
23  * GPL stuff.
24  *
25  * Revision 1.1.1.1  2000/08/21 01:05:31  moz
26  *
27  *
28  * Revision 1.1.1.1  2000/07/19 22:45:31  moz
29  * CVS Import
30  *
31  * Revision 1.36  2000/05/04 03:38:35  moz
32  * Got font faking working fairly sensibly.
33  *
34  * Revision 1.35  2000/05/04 02:27:01  moz
35  * Fix font scaling to some degree.
36  *
37  * Revision 1.34  2000/03/13 02:46:53  moz
38  * redraw_object fixes for lw.
39  *
40  * Revision 1.33  2000/03/09 23:43:52  moz
41  * No end styles for closed splines.
42  *
43  * Revision 1.32  2000/03/09 23:29:40  moz
44  * Don't allow joinstyle for splines/arcs.
45  *
46  * Revision 1.31  2000/03/06 02:28:49  moz
47  * Compile fixes.
48  *
49  * Revision 1.30  2000/03/06 01:06:18  moz
50  * Show arrow button in spline icon.
51  *
52  * Revision 1.29  2000/03/04 20:49:24  moz
53  * Fonts should be scaled by screen_ppi.
54  *
55  * Revision 1.28  2000/02/25 00:16:22  moz
56  * Set node fillcolour and fillstyle.
57  *
58  * Revision 1.27  2000/02/04 01:55:26  moz
59  * switch_icons(): don't rejig all icons if resulting from
60  * object selection.
61  *
62  * Revision 1.26  2000/02/01 16:52:20  moz
63  * switch_icons() fixes.
64  *
65  * Revision 1.25  2000/01/28 17:32:12  moz
66  * Inline cosround and sinround.
67  *
68  * Revision 1.24  2000/01/25 01:37:36  moz
69  * Don't set a border.
70  *
71  * Revision 1.23  2000/01/21 11:50:55  moz
72  * Removed dead comment.
73  *
74  * Revision 1.22  1999/11/15 23:01:21  moz
75  * Back out window groups change.
76  *
77  * Revision 1.21  1999/11/15 02:00:55  moz
78  * Rounded sin and cos for rotation.
79  * Name change.
80  * Set window group hint.
81  *
82  * Revision 1.20  1999/07/04 22:11:34  moz
83  * Place view icons correctly for more than one view.
84  *
85  * Revision 1.19  1999/06/30 15:32:59  moz
86  * Updated the view icon sizes in switch_icons().
87  *
88  * Revision 1.18  1999/05/22 02:49:08  moz
89  * Fixes for redrawing compounds.
90  *
91  * Revision 1.17  1999/05/19 17:06:43  moz
92  * 1.0 Checkin.
93  *
94  * Revision 1.16  1999/05/04 15:23:51  moz
95  * Moved snap() from obsolete grid.c
96  *
97  * Revision 1.15  1999/05/04 14:40:58  moz
98  * Allow arrow dialog for arc ellipses.
99  *
100  * Revision 1.14  1999/05/01 23:21:08  moz
101  * Changes to dynamic view_icon_window.
102  *
103  * Revision 1.13  1999/04/29 01:35:10  moz
104  * Make sure we don't try to switch icons to a user-defined colour.
105  *
106  * Revision 1.12  1999/04/29 00:26:17  moz
107  * Removed node box icon.
108  *
109  * Revision 1.11  1999/04/29 00:12:55  moz
110  * Disabled NO_INTERSECT in ellipse_box as it's broken.
111  *
112  * Revision 1.10  1999/04/28 03:29:39  moz
113  * coredump fix in switch_icons.
114  *
115  * Revision 1.9  1999/04/27 23:55:25  moz
116  * switch_icons() extended to set view icons from selected object.
117  *
118  * Revision 1.8  1999/04/27 18:48:19  moz
119  * Enable colour change for TEXT objects.
120  *
121  * Revision 1.7  1999/04/27 16:00:51  moz
122  * Flip angles in x and y axis.
123  *
124  * Revision 1.6  1999/04/27 04:24:30  moz
125  * draw_view parameters change.
126  *
127  * Revision 1.5  1999/04/27 04:12:44  moz
128  * Remove extraneous variable.
129  *
130  * Revision 1.4  1999/04/23 00:40:13  moz
131  * redraw_view_window change.
132  *
133  * Revision 1.3  1999/04/04 01:47:03  moz
134  * Merge multiple store redraw requests before sending.
135  *
136  * Revision 1.2  1999/04/03 23:13:51  moz
137  * Don't show Set Spline icon when in Pointer mode (editing).
138  *
139  * Revision 1.1  1999/03/30 00:07:04  moz
140  * Initial revision
141  *
142  */
143 
144 #include <stdlib.h>
145 #include <pwd.h>
146 #include <sys/types.h>
147 #include <unistd.h>
148 #include <string.h>
149 #include <math.h>
150 
151 #include "include/figurine.h"
152 #include "include/extern.h"
153 
154 /* for use of store_redraw_object */
155 static Boolean storedflag = FALSE;
156 static long slw = 0;
157 static long sjs = JoinRound;
158 static long sfarrow_w = 0;
159 static long sfarrow_h = 0;
160 static long sbarrow_w = 0;
161 static long sbarrow_h = 0;
162 static VRegion sbbox;
163 static Boolean scompound=FALSE;
164 
165 void
free_list(void * v)166 free_list(void *v)
167 {
168 	freelist = add_to_list(freelist,0,0,v);
169 }
170 
171 /* set X clipping region  */
172 inline void
set_clip(GC agc,int x,int y,int w,int h)173 set_clip(GC agc, int x, int y, int w, int h)
174 {
175 	static XRectangle rectangles[1];
176 
177 	rectangles[0].x = 0;
178 	rectangles[0].y = 0;
179 	rectangles[0].width = w;
180 	rectangles[0].height = h;
181 
182 	XSetClipRectangles(display,agc, x, y, rectangles, 1, Unsorted);
183 }
184 
185 void
set_clip_v(GC agc,View * v,VRegion bbox)186 set_clip_v(GC agc, View *v, VRegion bbox)
187 {
188 	XRectangle rectangles[1];
189 
190 	rectangles[0].x = 0;
191 	rectangles[0].y = 0;
192 	rectangles[0].width = XD2P(bbox.x2,v)-XD2P(bbox.x1,v)+1;
193 	rectangles[0].height = YD2P(bbox.y2,v)-YD2P(bbox.y1,v)+1;
194 	XSetClipRectangles(display,agc, XD2P(bbox.x1,v), YD2P(bbox.y1,v), rectangles, 1, Unsorted);
195 }
196 
197 /* determine user's $HOME  */
198 void
home_directory(char * result)199 home_directory(char *result)
200 {
201 	uid_t uid;
202 	struct passwd *password = NULL;
203 	char *tmp;
204 
205 	if ((tmp=getenv("HOME"))!=NULL)
206 		{
207 		strcpy(result, tmp);
208 		return;
209 		}
210 	else
211 		{
212 		if ((tmp=getenv("USER"))!=NULL)
213 			{
214 			password = getpwnam(tmp);
215 			}
216 		else
217 			{
218 			uid = getuid();
219 			password = getpwuid(uid);
220 			};
221 		};
222 
223 	if (password)
224 		{
225 		strcpy(result,password->pw_dir);
226 		return;
227 		};
228 
229 	*result = '\0';
230 }
231 
232 inline Boolean
is_in_box(int px,int py,int x,int y,int w,int h)233 is_in_box(int px, int py, int x, int y, int w, int h)
234 {
235 	return (px>=x && px <=x+w && py>=y && py<=y+h);
236 }
237 
238 inline Boolean
is_in_bbox(int px,int py,int x1,int y1,int x2,int y2)239 is_in_bbox(int px, int py, int x1, int y1, int x2, int y2)
240 {
241 	return (px>=x1 && px<=x2 && py>=y1 && py<=y2);
242 }
243 
244 inline Boolean
is_in_object(long px,long py,Object * ob)245 is_in_object(long px, long py, Object *ob)
246 {
247 	return (px>=ob->bbox.x1 && px<=ob->bbox.x2 && py>=ob->bbox.y1 && py<=ob->bbox.y2);
248 }
249 
250 Boolean
is_in_object_p(View * v,long x,long y,Object * ob)251 is_in_object_p(View *v, long x, long y, Object *ob)
252 {
253 	return is_in_object(XP2D(x,v), YP2D(y,v), ob);
254 }
255 
256 /* usage is_contained(outer_box, inner_box)  */
257 inline Boolean
is_contained(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4)258 is_contained(int x1, int y1, int x2, int y2,int x3, int y3, int x4, int y4)
259 {
260 	return (is_in_bbox(x3,y3,x1,y1,x2,y2) && is_in_bbox(x4,y4,x1,y1,x2,y2));
261 }
262 
263 /* usage bbox = merge_boxes(box1, box2)  */
264 /* box1 box2 must both be normalised  */
265 VRegion
merge_boxes(VRegion b1,VRegion b2)266 merge_boxes(VRegion b1, VRegion b2)
267 {
268 	VRegion r;
269 
270 	/* this works because b1,b2 are normalised, right ? */
271 	r.x1 = min(b1.x1, b2.x1);
272 	r.y1 = min(b1.y1, b2.y1);
273 	r.x2 = max(b1.x2, b2.x2);
274 	r.y2 = max(b1.y2, b2.y2);
275 
276 	return r;
277 }
278 
279 inline Boolean
is_contained_v(VRegion a,VRegion b)280 is_contained_v(VRegion a, VRegion b)
281 {
282 	return (is_in_bbox(b.x1,b.y1,a.x1,a.y1,a.x2,a.y2) && is_in_bbox(b.x2,b.y2,a.x1,a.y1,a.x2,a.y2));
283 }
284 
285 /* determine whether two boxes intersect  */
286 inline Boolean
intersects(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4)287 intersects(int x1, int y1, int x2, int y2,int x3, int y3, int x4, int y4)
288 {
289 	/* first test if a corner is in the other rectangle */
290 	if (is_in_bbox(x1,y1,x3,y3,x4,y4))
291 		return TRUE;
292 
293 	if (is_in_bbox(x2,y2,x3,y3,x4,y4))
294 		return TRUE;
295 
296 	if (is_in_bbox(x1,y2,x3,y3,x4,y4))
297 		return TRUE;
298 
299 	if (is_in_bbox(x2,y1,x3,y3,x4,y4))
300 		return TRUE;
301 
302 	/* if contained, TRUE  */
303 
304 	if (is_contained(x1,y1,x2,y2,x3,y3,x4,y4))
305 		return TRUE;
306 
307 	if (is_contained(x3,y3,x4,y4,x1,y1,x2,y2))
308 		return TRUE;
309 
310 	/* better double check for other rectangle */
311 	if (is_in_bbox(x3,y3,x1,y1,x2,y2))
312 		return TRUE;
313 
314 	if (is_in_bbox(x4,y4,x1,y1,x2,y2))
315 		return TRUE;
316 
317 	if (is_in_bbox(x3,y4,x1,y1,x2,y2))
318 		return TRUE;
319 
320 	if (is_in_bbox(x4,y3,x1,y1,x2,y2))
321 		return TRUE;
322 
323 	/* if contained, TRUE  */
324 
325 	if (((x3>=x1 && x3<=x2) || (x4>=x1 && x4<=x2)) && y3<=y1 && y4>=y2)
326 		return TRUE;
327 
328 	if (((y3>=y1 && y3<=y2) || (y4>=y1 && y4<=y2)) && x3<=x1 && x4>=x2)
329 		return TRUE;
330 
331 	return FALSE;
332 }
333 
334 Boolean
intersects_v(VRegion v1,VRegion v2)335 intersects_v(VRegion v1, VRegion v2)
336 {
337 	return intersects(v1.x1,v1.y1,v1.x2,v1.y2, v2.x1,v2.y1,v2.x2,v2.y2);
338 }
339 
340 /* normalised rectangle !!!  */
341 /* forces a point to be in the box x1,y1-x2,y2  */
342 void
force_in_box(int * xp,int * yp,int x1,int y1,int x2,int y2)343 force_in_box(int *xp, int *yp, int x1, int y1, int x2, int y2)
344 {
345 	if (*xp < x1 || *xp > x2)
346 		*xp = (x1+x2)/2;
347 
348 	if (*yp < y1 || *yp > y2)
349 		*yp = (y1+y2)/2;
350 
351 }
352 
353 void
force_in_box_l(long * xp,long * yp,long x1,long y1,long x2,long y2)354 force_in_box_l(long *xp, long *yp, long x1, long y1, long x2, long y2)
355 {
356 	*xp = max(x1,*xp);
357 	*xp = min(x2,*xp);
358 	*yp = max(y1,*yp);
359 	*yp = min(y2,*yp);
360 
361 }
362 
363 /* simple equation of line */
364 signed long
line_x_at_y(long x1,long y1,long x2,long y2,long y)365 line_x_at_y(long x1, long y1, long x2, long y2, long y)
366 {
367 	return (signed long)( ((((double)(x2-x1))/(y2-y1))*(y-y1))+x1);
368 }
369 
370 /* simple equation of line */
371 signed long
line_y_at_x(long x1,long y1,long x2,long y2,long x)372 line_y_at_x(long x1, long y1, long x2, long y2, long x)
373 {
374 	return (signed long)( ((((double)(y2-y1))/(x2-x1))*(x-x1))+y1);
375 }
376 
377 /* rounded sin and cos useful for us*/
378 inline double
sinround(double angle)379 sinround(double angle)
380 {
381 	double a=sin(angle);
382 
383 	if (between(a,-0.03,0.03))
384 		return 0.0;
385 
386 	if (between(a,0.97,1.0))
387 		return 1.0;
388 
389 	if (between(a,-1.0,-0.97))
390 		return -1.0;
391 
392 	return a;
393 }
394 
395 inline double
cosround(double angle)396 cosround(double angle)
397 {
398 	double a=cos(angle);
399 
400 	if (between(a,-0.03,0.03))
401 		return 0.0;
402 
403 	if (between(a,0.97,1.0))
404 		return 1.0;
405 
406 	if (between(a,-1.0,-0.97))
407 		return -1.0;
408 
409 	return a;
410 }
411 
412 /* flip an angle in the x axis */
413 double
flip_angle_x(double angle)414 flip_angle_x(double angle)
415 {
416 	double x,y;
417 
418 	x = - cosround(angle);
419 	y = sinround (angle);
420 
421 	angle = atan2(y,x);
422 
423 	while (angle>2*PI)
424 		angle -= 2*PI;
425 
426 	while (angle<0)
427 		angle += 2*PI;
428 
429 	return angle;
430 }
431 
432 /* flip an angle in the y axis */
433 double
flip_angle_y(double angle)434 flip_angle_y(double angle)
435 {
436 	double x,y;
437 
438 	x = cosround(angle);
439 	y = - sinround(angle);
440 
441 	angle = atan2(y,x);
442 
443 	while (angle>2*PI)
444 		angle -= 2*PI;
445 
446 	while (angle<0)
447 		angle += 2*PI;
448 
449 	return angle;
450 }
451 
452 /* normalise a rectangle described by two points  */
453 void
normalise_rectangle(long * x1,long * y1,long * x2,long * y2)454 normalise_rectangle(long *x1, long *y1, long *x2, long *y2)
455 {
456 	long a;
457 
458 	if (*x1<=*x2 && *y1<=*y2) /* line from tl to br  */
459 		return;
460 
461 	if (*x1>=*x2 && *y1>=*y2) /* line from br to tl  */
462 		{
463 		a = *x2;
464 		*x2 = *x1;
465 		*x1 = a;
466 		a = *y2;
467 		*y2 = *y1;
468 		*y1 = a;
469 		return;
470 		};
471 
472 	if (*x1<=*x2 && *y1>=*y2) /* line from bl to tr  */
473 		{
474 		a = *y1;
475 		*y1 = *y2;
476 		*y2 = a;
477 		return;
478 		};
479 
480 	if (*x1>=*x2 && *y1<=*y2) /* line from tr to bl */
481 		{
482 		a = *x1;
483 		*x1 = *x2;
484 		*x2 = a;
485 		return;
486 		};
487 
488 }
489 
490 /* send a redraw for a section */
491 void
send_redraw(WindowStruct * window,int x,int y,int w,int h)492 send_redraw(WindowStruct *window, int x, int y, int w, int h)
493 {
494 	XEvent ev;
495 
496 	set_clip(ugc, x, y, w, h);
497 	set_clip(handlegc, x, y, w, h);
498 	set_clip(blackxorgc, x, y, w, h);
499 	set_clip(dashgc, x, y, w, h);
500 	set_clip(fillgc, x, y, w, h);
501 
502 	ev.type = Expose;
503 	ev.xexpose.window = window->win;
504 	ev.xexpose.x = x;
505 	ev.xexpose.y = y;
506 	ev.xexpose.width = w;
507 	ev.xexpose.height = h;
508 	ev.xexpose.count = 0;
509 
510 	XSendEvent(display, window->win, False, 0, &ev);
511 }
512 
513 void
send_redraw_v(View * view,VRegion bb)514 send_redraw_v(View *view, VRegion bb)
515 {
516 	int x1,y1,x2,y2;
517 	List l;
518 
519 	l = view->doc->views;
520 
521 	while (l!=NULL)
522 		{
523 
524 		x1 = XD2P(bb.x1,VIEW(l));
525 		y1 = YD2P(bb.y1,VIEW(l));
526 		x2 = XD2P(bb.x2,VIEW(l));
527 		y2 = YD2P(bb.y2,VIEW(l));
528 
529 		if (intersects(x1,y1,x2,y2,0,0,(int)VIEW(l)->draw_window->w,(int)VIEW(l)->draw_window->h))
530 			{
531 			x1 = max(0,x1);
532 			y1 = max(0,y1);
533 			x2 = min(x2-x1,(int)VIEW(l)->draw_window->w);
534 			y2 = min(y2-y1,(int)VIEW(l)->draw_window->h);
535 
536 			send_redraw(VIEW(l)->draw_window, x1, y1, x2+1, y2+1);
537 			};
538 		l = l->next;
539 		};
540 }
541 
542 /* store shape of first object  */
543 void
store_redraw_object(Object * ob)544 store_redraw_object(Object *ob)
545 {
546 	/* if we already have stored, but not sent, merge the requests */
547 	if (storedflag)
548 		sbbox = merge_boxes(sbbox,ob->bbox);
549 	else
550 		sbbox = ob->bbox;
551 
552 	if (ob->type!=COMPOUND)
553 		{
554 		slw = ob->lw;
555 		sjs = ob->js;
556 
557 		if (ob->farrow!=NULL)
558 			{
559 			sfarrow_w = ob->farrow->w;
560 			sfarrow_h = ob->farrow->h;
561 			}
562 		else
563 			sfarrow_w = 0; /* checked for  */
564 
565 		if (ob->barrow!=NULL)
566 			{
567 			sbarrow_w = ob->barrow->w;
568 			sbarrow_h = ob->barrow->h;
569 			}
570 		else
571 			sbarrow_w = 0; /* checked for  */
572 		}
573 	else
574 		scompound=TRUE;
575 
576 	storedflag = TRUE;
577 }
578 
579 /* merge ob with value stored above  */
580 /* and send redraw  */
581 void
send_stored_redraw_object(View * view,Object * ob)582 send_stored_redraw_object(View *view, Object *ob)
583 {
584 	VRegion bb;
585 	VRegion sbb;
586 	int x1,y1,x2,y2;
587 	long v =0;
588 	List l;
589 	long lw;
590 
591 	storedflag = FALSE;
592 	l = view->doc->views;
593 
594 	while (l!=NULL)
595 		{
596 		v = 0;
597 		if (ob->type == POLYLINE || ob->type==POLYGON)
598 			{
599 			lw = I2D((((double)ob->lw)/80.0),VIEW(l));
600 			/* if join miter, the point can be miles away from bb  */
601 			if (ob->js==JoinMiter)
602 				lw = I2D((((double)ob->lw)/80.0),VIEW(l))*6;
603 
604 			if (ob->farrow!=NULL)
605 				v = I2D((((double)max(ob->farrow->w,ob->farrow->h))/80.0),VIEW(l));
606 
607 			if (ob->barrow!=NULL)
608 				v = max(v, I2D((((double)max(ob->barrow->w,ob->barrow->h))/80.0),VIEW(l)));
609 
610 			lw += v;
611 			}
612 		else
613 			lw = 0;
614 
615 		bb.x1 = ob->bbox.x1-lw;
616 		bb.y1 = ob->bbox.y1-lw;
617 		bb.x2 = ob->bbox.x2+lw;
618 		bb.y2 = ob->bbox.y2+lw;
619 
620 		/* account for handles in editing mode  */
621 		bb.x1 = bb.x1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
622 		bb.y1 = bb.y1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
623 		bb.x2 = bb.x2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
624 		bb.y2 = bb.y2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
625 
626 		v = 0;
627 		if (!scompound)
628 			{
629 			lw = I2D((((double)slw)/80.0),VIEW(l));
630 			/* if join miter, the point can be miles away from bb  */
631 			if (sjs==JoinMiter)
632 				lw = I2D((((double)slw)/80.0),VIEW(l))*6;
633 
634 			if (sfarrow_w!=0)
635 				v = I2D((((double)max(sfarrow_w,sfarrow_h))/80.0),VIEW(l));
636 			if (sbarrow_w!=0)
637 				v = I2D((((double)max(sbarrow_w,sbarrow_h))/80.0),VIEW(l));
638 
639 			lw += v;
640 			}
641 		else
642 			lw = I2D(1,VIEW(l));
643 
644 		sbb.x1 = sbbox.x1-lw;
645 		sbb.y1 = sbbox.y1-lw;
646 		sbb.x2 = sbbox.x2+lw;
647 		sbb.y2 = sbbox.y2+lw;
648 
649 		/* account for handles in editing mode  */
650 		sbb.x1 = sbb.x1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
651 		sbb.y1 = sbb.y1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
652 		sbb.x2 = sbb.x2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
653 		sbb.y2 = sbb.y2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
654 
655 		bb = merge_boxes(bb,sbb);
656 
657 		x1 = XD2P(bb.x1,VIEW(l));
658 		y1 = YD2P(bb.y1,VIEW(l));
659 		x2 = XD2P(bb.x2,VIEW(l));
660 		y2 = YD2P(bb.y2,VIEW(l));
661 
662 		if (intersects(x1,y1,x2,y2,0,0,(int)VIEW(l)->draw_window->w,(int)VIEW(l)->draw_window->h))
663 			{
664 			x1 = max(0,x1);
665 			y1 = max(0,y1);
666 			x2 = min(x2-x1,(int)VIEW(l)->draw_window->w);
667 			y2 = min(y2-y1,(int)VIEW(l)->draw_window->h);
668 
669 			send_redraw(VIEW(l)->draw_window, x1, y1, x2+1, y2+1);
670 			};
671 
672 		l = l->next;
673 		};
674 	scompound=FALSE;
675 }
676 
677 /* redraw the whole view window  */
678 void
redraw_view_window(View * view)679 redraw_view_window(View *view)
680 {
681 	XEvent xev;
682 
683 	/* remove queued redraw events on this window  */
684 	while (XCheckTypedWindowEvent(display, view->draw_window->win, Expose, &xev))
685 		;
686 
687 	draw_view(view,0,0,(long)view->draw_window->w,(long)view->draw_window->h);
688 }
689 
690 /* redraw an object, taking account
691 	of object properties */
692 void
send_redraw_object(View * view,Object * ob)693 send_redraw_object(View *view, Object *ob)
694 {
695 	VRegion bb;
696 	int x1,y1,x2,y2;
697 	long v =0;
698 	List l;
699 	long lw=0;
700 
701 	l = view->doc->views;
702 
703 	while (l!=NULL)
704 		{
705 		if (ob->type == POLYLINE || ob->type==POLYGON)
706 			{
707 			lw = I2D((((double)ob->lw)/80.0),VIEW(l));
708 			/* if join miter, the point can be miles away from bb  */
709 			if (ob->js==JoinMiter)
710 				lw = I2D((((double)ob->lw)/80.0),VIEW(l))*6;
711 
712 			if (ob->farrow!=NULL)
713 				v = I2D((((double)max(ob->farrow->w,ob->farrow->h))/80.0),VIEW(l));
714 
715 			if (ob->barrow!=NULL)
716 				v = max(v, I2D((((double)max(ob->barrow->w,ob->barrow->h))/80.0),VIEW(l)));
717 
718 			lw += v;
719 			}
720 		else
721 			lw = 0;
722 
723 		bb.x1 = ob->bbox.x1-lw;
724 		bb.y1 = ob->bbox.y1-lw;
725 		bb.x2 = ob->bbox.x2+lw;
726 		bb.y2 = ob->bbox.y2+lw;
727 
728 		/* account for handles in editing mode  */
729 		bb.x1 = bb.x1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
730 		bb.y1 = bb.y1-P2D(HANDLE_PIXEL_SIZE,VIEW(l));
731 		bb.x2 = bb.x2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
732 		bb.y2 = bb.y2+P2D(HANDLE_PIXEL_SIZE,VIEW(l));
733 
734 		x1 = XD2P(bb.x1,VIEW(l));
735 		y1 = YD2P(bb.y1,VIEW(l));
736 		x2 = XD2P(bb.x2,VIEW(l));
737 		y2 = YD2P(bb.y2,VIEW(l));
738 
739 		if (intersects(x1,y1,x2,y2,0,0,(int)VIEW(l)->draw_window->w,(int)VIEW(l)->draw_window->h))
740 			{
741 			x1 = max(0,x1);
742 			y1 = max(0,y1);
743 			x2 = min(x2-x1,(int)VIEW(l)->draw_window->w);
744 			y2 = min(y2-y1,(int)VIEW(l)->draw_window->h);
745 
746 			send_redraw(VIEW(l)->draw_window, x1, y1, x2+1, y2+1);
747 			};
748 		l = l->next;
749 		};
750 }
751 
752 /* configure a window  */
753 void
send_configure(WindowStruct * window,int x,int y,int w,int h)754 send_configure(WindowStruct *window, int x, int y, int w, int h)
755 {
756 	XEvent ev;
757 
758 	ev.type = ConfigureNotify;
759 	ev.xconfigure.window = window->win;
760 	ev.xconfigure.x = x;
761 	ev.xconfigure.y = y;
762 	ev.xconfigure.width = w;
763 	ev.xconfigure.height = h;
764 	ev.xconfigure.border_width = 0;
765 	ev.xconfigure.above = None;
766 	ev.xconfigure.override_redirect = FALSE;
767 
768 	XSendEvent(display, window->win, False, 0, &ev);
769 }
770 
771 /* set a window cursor type  */
772 void
set_window_cursor(Window win,int c)773 set_window_cursor(Window win, int c)
774 {
775 	Cursor tc;
776 	XSetWindowAttributes wattr;
777 
778 	tc = XCreateFontCursor(display,(uint)c);
779 	wattr.cursor = tc;
780 
781 	XChangeWindowAttributes(display,win, CWCursor, &wattr);
782 }
783 
784 /* create a normal window  */
785 Boolean
create_window(WindowStruct * window,Window parent,List * list,int type,void * data)786 create_window(WindowStruct *window, Window parent, List *list, int type, void *data)
787 {
788 	List templ;
789 	XGCValues gcvalues;
790 
791 	XGetGCValues(display, window->gc, (GCForeground | GCBackground), &gcvalues);
792 
793 	window->win = XCreateSimpleWindow(display, parent,
794 								window->x,window->y,window->w,window->h,
795 								0, gcvalues.foreground,
796 								gcvalues.background);
797 
798 	set_window_cursor(window->win,window->cursor_num);
799 
800 
801 	if ((window->wm_hints = XAllocWMHints())!=NULL)
802 		{
803 		window->wm_hints->initial_state = NormalState;
804 		window->wm_hints->input = True;
805 		window->wm_hints->flags = StateHint | InputHint;
806 		XSetWMHints(display,window->win, window->wm_hints);
807 		};
808 	if ((window->size_hints = XAllocSizeHints())!=NULL)
809 		{
810 		window->size_hints->flags = PPosition | PSize | PMinSize;
811 		window->size_hints->min_width = 50;
812 		window->size_hints->min_height = 50;
813 		XSetWMNormalHints(display,window->win,window->size_hints);
814 		};
815 
816 	XSelectInput(display, window->win, KeyPressMask | ButtonPressMask
817 					 | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask
818 					 | PointerMotionMask | ButtonMotionMask
819 					 | StructureNotifyMask
820 					 | ExposureMask | StructureNotifyMask | FocusChangeMask);
821 
822 	if (parent==RootWindow(display,screen))
823 		XSetWMProtocols(display,window->win, &windelete, 1);
824 
825 	XMoveResizeWindow(display, window->win, window->x, window->y, window->w, window->h);
826 
827 	templ = add_to_list(*list,(ulong)window->win,(ulong)type,data);
828 
829 	if (templ==NULL)
830 		return FALSE;
831 
832 	*list = templ;
833 
834 	return TRUE;
835 }
836 
837 /* set the window title  */
838 void
set_window_name(WindowStruct * window,char * name)839 set_window_name(WindowStruct *window,char *name)
840 {
841 	XTextProperty textprop;
842 
843 	XStringListToTextProperty(&name,1,&textprop);
844 
845 	XSetWMName(display,window->win, &textprop);
846 }
847 
848 /* constrain a line to 90 degrees  */
849 inline void
constrain_line(long x1,long y1,long * x2,long * y2)850 constrain_line(long x1, long y1, long *x2, long *y2)
851 {
852 	((abs(*x2-x1))>(abs(*y2-y1))) ? (*y2 = y1) : (*x2 = x1);
853 }
854 
855 /* constrain proportionally  */
856 inline void
constrain_resize(double * xr,double * yr)857 constrain_resize(double *xr, double *yr)
858 {
859 	(*xr>*yr) ? (*yr= *xr) : (*xr = *yr);
860 }
861 
862 /* constrain proportionally  */
863 inline void
constrain_ellipse(long * xr,long * yr)864 constrain_ellipse(long *xr, long *yr)
865 {
866 	(*xr>*yr) ? (*yr= *xr) : (*xr = *yr);
867 }
868 
869 /* constrain proportionally  */
870 inline void
constrain_rectangle(long x1,long y1,long * x2,long * y2)871 constrain_rectangle(long x1, long y1, long *x2, long *y2)
872 {
873 	((abs(*x2-x1))>(abs(*y2-y1))) ? (*y2 = (*x2-x1+y1)) : (*x2 = (*y2-y1+x1));
874 }
875 
876 void
deselect_everything()877 deselect_everything()
878 {
879 	List l;
880 	Boolean redraw=FALSE;
881 	Object *ob=NULL;
882 
883 	l = state.views;
884 
885 	while (l!=NULL)
886 		{
887 		if (VIEW(l)->selected_object!=NULL)
888 			{
889 			redraw = TRUE;
890 			ob = VIEW(l)->selected_object->ob;
891 			VIEW(l)->selected_object = NULL;
892 			};
893 
894 		if (VIEW(l)->highlighted_object!=NULL)
895 			{
896 			redraw = TRUE;
897 			ob = VIEW(l)->highlighted_object->ob;
898 			VIEW(l)->highlighted_object = NULL;
899 			};
900 
901 		if (VIEW(l)->edited_object!=NULL)
902 			{
903 			redraw = TRUE;
904 			ob = VIEW(l)->edited_object->ob;
905 			};
906 
907 		if (redraw)
908 			send_redraw_object(VIEW(l), ob);
909 
910 		VIEW(l)->edited_object = NULL;
911 
912 		redraw = FALSE;
913 
914 		l = l->next;
915 		};
916 
917 }
918 
919 /* is a point x,y in an ellipse  */
920 Boolean
point_in_ellipse(long cx,long cy,long a,long b,long x,long y)921 point_in_ellipse(long cx, long cy, long a, long b, long x, long y)
922 {
923 	double xda = ((double)(x-cx))/((double)a);
924 	double ydb = ((double)(y-cy))/((double)b);
925 
926 	return ((xda*xda)+(ydb*ydb) < 1.0);
927 }
928 
929 /* does a box and an ellipse intersect  */
930 int
ellipse_box(long cx,long cy,long a,long b,long x1,long y1,long x2,long y2)931 ellipse_box(long cx, long cy, long a, long b, long x1, long y1, long x2, long y2)
932 {
933    /* returns CONTAINS if ellipse contains box */
934    /* returns CONTAINED if box contains ellipse or intersects */
935 
936    if (point_in_ellipse(cx,cy,a,b,x1,y1) && point_in_ellipse(cx,cy,a,b,x2,y1) &&
937 		 point_in_ellipse(cx,cy,a,b,x1,y2) && point_in_ellipse(cx,cy,a,b,x2,y2))
938 		return CONTAINS;
939 
940 	return CONTAINED;
941 }
942 
943 /* get a font of specified size and type  */
944 /* nb point size is *scaled* point size  */
945 VFont *
get_font(int fontnumber,int pointsize)946 get_font(int fontnumber, int pointsize)
947 {
948 	char name[100];
949 	char a[10];
950 	int realsize;
951 	VFont *vf;
952 	List l;
953 	char name2[100];
954 	char **availfonts;
955 	int bfontsize=0;
956 	int numf=0;
957 
958 	if (pointsize < 20)
959 		return NULL;
960 	/* i.e. greek the text, no font  */
961 
962 	if (pointsize >3000)
963 		return NULL;
964 
965 	l = afonts;
966 
967 	while (l)
968 		{
969 		if (fontnumber==FONT(l)->num && FONT(l)->size==pointsize)
970 			break;
971 		l = l->next;
972 		};
973 
974 	if (l)
975 		return FONT(l);
976 
977 	/* this code is set up to get scaled bitmap fonts as per Nye A.3.2 */
978 	/* they look terrible but that's X for you */
979 
980 	/* -adobe-courier-bold-o-normal-*-10-100-75-75-m-60-iso8859-1 */
981 	/* -foundry-family-weight-slant-setwidth-"unknown"-pixels-10thpoints-hdpi-vdpi-spacing-avewidth-charset */
982 	vf = (VFont *)malloc(sizeof(VFont));
983 	vf->num = fontnumber;
984 	vf->size = pointsize;
985 	strcpy(name,fontnames[fontnumber][0]);
986 	realsize = ((int)((pointsize/(POINTS_IN_INCH))*screen_ppi));
987 	sprintf(a,"*-*-%d",realsize);
988 	strcat(name,a);
989 	/* REQUIRES the iso8859-1 to load */
990 	strcat(name,"-75-75-*-*-iso8859-1");
991 
992 	/* assignment used as truth value :) */
993 	if ((vf->x=XLoadQueryFont(display,name)))
994 		{
995 		afonts = add_to_list(afonts,0,0,(void *)vf);
996 		return vf;
997 		};
998 
999 	/* if we get here, font server won't scale bitmaps or font really isn't available */
1000 	/* there are a surprising number of font servers out there not doing this :( */
1001 
1002 	/* search for the nearest */
1003 
1004 	strcpy(name2,fontnames[fontnumber][0]);
1005 	strcat(name2,"*-*-*-*-*-*-*-iso8859-1");
1006 	/* not ordered by size */
1007 	availfonts = XListFonts(display, name2, 200, &numf);
1008 	if (availfonts)
1009 		{
1010 		while (numf)
1011 			{
1012 			char *f = *(availfonts+numf-1);
1013 			int asize;
1014 			char *g;
1015 
1016 
1017 			while (*f!='\0' && (*f!='-' || *(f+1)!='-'))
1018 				f++;
1019 
1020 			if (*f=='\0')
1021 				continue;
1022 
1023 			/* -- */
1024 			f+=2;
1025 			/* skip pixel size */
1026 			while(*f!='\0' && *f!='-')
1027 				f++;
1028 
1029 			if (*f=='\0')
1030 				continue;
1031 
1032 			/* - */
1033 			g = ++f;
1034 
1035 			while (*g!='\0' && *g!='-')
1036 				g++;
1037 
1038 			*g='\0';
1039 			/* pointsize */
1040 			sscanf(f,"%d",&asize);
1041 			/* pick biggest smaller than realsize */
1042 			if (asize<=realsize && asize>bfontsize)
1043 				bfontsize=asize;
1044 
1045 			numf--;
1046 			};
1047 
1048 		if (bfontsize)
1049 			{
1050 			char name3[10];
1051 			/* XListFonts returns stuff like "-adobe-times-medium-r-normal--12-120" */
1052 			strcpy(name2,fontnames[fontnumber][0]);
1053 			sprintf(name3,"*-*-%d",bfontsize);
1054 			strcat(name2,name3);
1055 			strcat(name2,"-*-*-*-*-iso8859-1");
1056 			if ((vf->x=XLoadQueryFont(display,name2))!=NULL)
1057 				{
1058 				/* if the font is way out, warn user */
1059 				if (abs(bfontsize-realsize)>100)
1060 					fprintf(stderr,"figurine: warning: using font %s\nfigurine:instead of %s\n",name2,name);
1061 
1062 				XFreeFontNames(availfonts);
1063 				afonts = add_to_list(afonts,0,0,(void *)vf);
1064 				return vf;
1065 				};
1066 			};
1067 		/* failure */
1068 		XFreeFontNames(availfonts);
1069 		};
1070 
1071 	fprintf(stderr, "figurine: Could not load font %s\n", name);
1072 	free(vf);
1073 
1074 	/* careful not to infinitely recurse */
1075 	if (fontnumber!=0 && fontnumber!=35)
1076 		return get_font(0, pointsize); /* first try times replica  */
1077 
1078 	return NULL; /* ok, have to greek  */
1079 }
1080 
1081 /* switch the icons on every view window to reflect current
1082 	drawing mode / object */
1083 
1084 #define off(a) stk_hide_icon((a),v->view_icon_window->win)
1085 #define onx(a) do { stk_show_icon((a),v->view_icon_window->win); \
1086 		stk_set_x((a),v->view_icon_window->win, x); \
1087 		x += VIEW_ICON_WIDTH; } while (0)
1088 
1089 
1090 void
switch_icons(View * us)1091 switch_icons(View *us)
1092 {
1093 	List l;
1094 	View *v;
1095 	int a=state.current_icon;
1096 	IconType temp;
1097 	int x;
1098 
1099 	l = state.views;
1100 
1101 	while (l!=NULL)
1102 		{
1103 		v = VIEW(l);
1104 		x=4*VIEW_ICON_WIDTH;
1105 
1106 		if (v!=us && us->selected_object && state.current_icon==POINTERICON)
1107 			{
1108 			l=l->next;
1109 			continue;
1110 			};
1111 
1112 		off(LINESTYLEICON);
1113 		off(COLOURICON);
1114 		off(FILLSTYLEICON);
1115 		off(FILLCOLOURICON);
1116 		off(LINEWIDTHICON);
1117 		off(ENDSTYLEICON);
1118 		off(JOINSTYLEICON);
1119 		off(ARROWICON);
1120 		off(SETPOLYGONICON);
1121 		off(FONTICON);
1122 		off(JUSTIFICATIONICON);
1123 		off(SETSPLINEICON);
1124 		off(ROUNDBOXICON);
1125 		off(SETARCELLIPSEICON);
1126 
1127 		if (state.current_icon!=POINTERICON || !v->selected_object)
1128 			a = state.current_icon;
1129 
1130 		if (state.current_icon==POINTERICON && v->selected_object!=NULL)
1131 			{
1132 			switch (v->selected_object->ob->type)
1133 				{
1134 				case ARCELLIPSE: a = ARCELLIPSEICON; break;
1135 				case ELLIPSE: a = ELLIPSEICON; break;
1136 				case POLYLINE: a = POLYLINEICON; break;
1137 				case POLYGON: a = POLYGONICON; break;
1138 				case SPLINE:
1139 				case ARC:
1140 					a = SPLINEICON;
1141 					break;
1142 
1143 				case TEXT:
1144 					if (v->selected_object->ob->ob.text.node)
1145 						{
1146 						onx(LINESTYLEICON);
1147 						onx(COLOURICON);
1148 						onx(FILLSTYLEICON);
1149 						onx(FILLCOLOURICON);
1150 						onx(LINEWIDTHICON);
1151 						}
1152 					else
1153 						{
1154 						a = 0;
1155 						onx(COLOURICON);
1156 						};
1157 					break;
1158 
1159 
1160 				case ROUNDBOX:
1161 					onx(LINESTYLEICON);
1162 					onx(COLOURICON);
1163 					onx(FILLSTYLEICON);
1164 					onx(FILLCOLOURICON);
1165 					onx(LINEWIDTHICON);
1166 					onx(ROUNDBOXICON);
1167 					break;
1168 
1169 				default: a = state.current_icon; break;
1170 				};
1171 			};
1172 
1173 		switch(a)
1174 			{
1175 			case 0: break;
1176 			case COMPOUNDICON: break;
1177 
1178 			case POLYLINEICON:
1179 				onx(LINESTYLEICON); onx(COLOURICON);
1180 				onx(LINEWIDTHICON); onx(ENDSTYLEICON);
1181 				onx(JOINSTYLEICON); onx(ARROWICON);
1182 				break;
1183 
1184 			case ARCELLIPSEICON:
1185 				onx(LINESTYLEICON); onx(COLOURICON);
1186 				onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1187 				onx(LINEWIDTHICON); onx(ENDSTYLEICON);
1188 				onx(SETARCELLIPSEICON); onx(ARROWICON);
1189 				break;
1190 
1191 			case ELLIPSEICON:
1192 			case BOXELLIPSEICON:
1193 				onx(LINESTYLEICON); onx(COLOURICON);
1194 				onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1195 				onx(LINEWIDTHICON);
1196 				break;
1197 
1198 			case RECTANGLEICON:
1199 				onx(LINESTYLEICON); onx(COLOURICON);
1200 				onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1201 				onx(LINEWIDTHICON); onx(JOINSTYLEICON);
1202 				onx(ROUNDBOXICON);
1203 				break;
1204 
1205 			case POLYGONICON:
1206 				onx(LINESTYLEICON); onx(COLOURICON);
1207 				onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1208 				onx(LINEWIDTHICON); onx(JOINSTYLEICON);
1209 				if (state.current_icon!=POINTERICON)
1210 					onx(SETPOLYGONICON);
1211 				break;
1212 
1213 			case SPLINEICON:
1214 				/* yuk */
1215 				onx(LINESTYLEICON); onx(COLOURICON);
1216 				if (state.current_icon!=POINTERICON || v->selected_object)
1217 					{
1218 					onx(FILLSTYLEICON);
1219 					onx(FILLCOLOURICON);
1220 					};
1221 				onx(LINEWIDTHICON);
1222 				if (state.current_icon!=POINTERICON || v->selected_object)
1223 					{
1224 					onx(ENDSTYLEICON);
1225 					onx(ARROWICON);
1226 					};
1227 				if (state.current_icon!=POINTERICON)
1228 					onx(SETSPLINEICON);
1229 				break;
1230 
1231 			case TEXTICON:
1232 				onx(COLOURICON); onx(FONTICON);
1233 				onx(JUSTIFICATIONICON);
1234 				break;
1235 
1236 			case NODEICON:
1237 				onx(LINESTYLEICON); onx(COLOURICON);
1238 				onx(FILLSTYLEICON); onx(FILLCOLOURICON);
1239 				onx(LINEWIDTHICON); onx(FONTICON);
1240 				onx(JUSTIFICATIONICON);
1241 				break;
1242 
1243 			case ARCICON:
1244 				onx(LINESTYLEICON); onx(COLOURICON);
1245 				onx(LINEWIDTHICON); onx(ENDSTYLEICON);
1246 				onx(ARROWICON);
1247 				onx(FONTICON); onx(JUSTIFICATIONICON);
1248 				break;
1249 			};
1250 
1251 		if (v->selected_object!=NULL)
1252 			{
1253 			Object *ob = v->selected_object->ob;
1254 
1255 			/* linestyle  */
1256 			if (ob->type!=TEXT && ob->type!=COMPOUND)
1257 				{
1258 				switch((signed)ob->ls)
1259 					{
1260 					case SOLID: temp=SOLIDICON; break;
1261 					case DASHED: temp=DASHEDICON; break;
1262 					case DOTTED: temp=DOTTEDICON; break;
1263 					case DASH_DOTTED: temp=DASHDOTTEDICON; break;
1264 					case DASH_DOUBLE_DOTTED: temp=DASHDOUBLEDOTTEDICON; break;
1265 					case DASH_TRIPLE_DOTTED: temp=DASHTRIPLEDOTTEDICON; break;
1266 					case -1: temp=SOLIDICON; break;
1267 					default: temp=0; break;
1268 					};
1269 				stk_swap_pixmap(LINESTYLEICON, v->view_icon_window->win,(signed)temp,linestyle_menu);
1270 				};
1271 
1272 			/* endstyle  */
1273 			if (ob->type==POLYLINE || ((ob->type==SPLINE || ob->type==ARC) && !ob->ob.spline.closed))
1274 			 	{
1275 				switch(ob->es)
1276 					{
1277 					case CapRound: temp=ROUNDENDICON; break;
1278 					case CapButt: temp=FLATENDICON; break;
1279 					case CapProjecting: temp=PROJECTINGENDICON; break;
1280 					default: temp=0; break;
1281 					};
1282 				stk_swap_pixmap(ENDSTYLEICON, v->view_icon_window->win,(signed)temp,endstyle_menu);
1283 				};
1284 
1285 			/* joinstyle  */
1286 			if (ob->type==POLYGON || ob->type==POLYLINE)
1287 			 	{
1288 				switch(ob->js)
1289 					{
1290 					case JoinRound: temp=ROUNDJOINICON; break;
1291 					case JoinMiter: temp=SHARPJOINICON; break;
1292 					case JoinBevel: temp=BEVELJOINICON; break;
1293 					default: temp=0; break;
1294 					};
1295 				stk_swap_pixmap(JOINSTYLEICON, v->view_icon_window->win,(signed)temp,joinstyle_menu);
1296 				};
1297 
1298 			/* colour  */
1299 			if (ob->type==ARCELLIPSE || ob->type==POLYGON ||
1300 				ob->type==ROUNDBOX || ob->type==ELLIPSE ||
1301 				ob->type==POLYLINE || ob->type==TEXT ||
1302 				ob->type==SPLINE || ob->type==ARC)
1303 				{
1304 				if (ob->colour<332)
1305 					{
1306 					stk_swap_pixmap(COLOURICON, v->view_icon_window->win,(signed)ob->colour,colour_menu);
1307 					v->colour = ob->colour;
1308 					};
1309 				};
1310 
1311 			/* fill colour  */
1312 			if (ob->type==ARCELLIPSE || ob->type==POLYGON ||
1313 				 ob->type==ROUNDBOX || ob->type==ELLIPSE ||
1314 				 ob->type==SPLINE || ob->type==ARC)
1315 				{
1316 				if (ob->fillcolour<432)
1317 					{
1318 					stk_swap_pixmap(FILLCOLOURICON, v->view_icon_window->win,(signed)ob->fillcolour,fillcolour_menu);
1319 					v->fillcolour = ob->fillcolour;
1320 					};
1321 				};
1322 
1323 			/* fill style  */
1324 			if (ob->type==ARCELLIPSE || ob->type==POLYGON ||
1325 				 ob->type==ROUNDBOX || ob->type==ELLIPSE ||
1326 				 ob->type==SPLINE || ob->type==ARC)
1327 				{
1328 				if (ob->fs==-1)
1329 					stk_swap_pixmap(FILLSTYLEICON, v->view_icon_window->win, 243, fillstyle_menu);
1330 				else
1331 					stk_swap_pixmap(FILLSTYLEICON, v->view_icon_window->win, STARTOFFILLSTYLES+ob->fs, fillstyle_menu);
1332 				v->fillstyle = ob->fs;
1333 				};
1334 
1335 			/* node special cases */
1336 			if (ob->type==TEXT && ob->ob.text.node)
1337 				{
1338 				if (ob->fillcolour<432)
1339 					{
1340 					stk_swap_pixmap(FILLCOLOURICON, v->view_icon_window->win,
1341 							(signed)ob->ob.text.ellipse->fillcolour, fillcolour_menu);
1342 					v->fillcolour = ob->ob.text.ellipse->fillcolour;
1343 					};
1344 
1345 				if (ob->ob.text.ellipse->fs==-1)
1346 					stk_swap_pixmap(FILLSTYLEICON, v->view_icon_window->win, 243, fillstyle_menu);
1347 				else
1348 					stk_swap_pixmap(FILLSTYLEICON, v->view_icon_window->win,
1349 							STARTOFFILLSTYLES+ob->ob.text.ellipse->fs, fillstyle_menu);
1350 				v->fillstyle = ob->ob.text.ellipse->fs;
1351 				};
1352 
1353 			/* arcellipse type  */
1354 			if (ob->type==ARCELLIPSE)
1355 				{
1356 				if (ob->ob.arcellipse.open)
1357 					stk_swap_pixmap(SETARCELLIPSEICON, v->view_icon_window->win, OPENARCELLIPSEICON, setarcellipse_menu);
1358 				else
1359 					stk_swap_pixmap(SETARCELLIPSEICON, v->view_icon_window->win, CLOSEDARCELLIPSEICON, setarcellipse_menu);
1360 				v->openarc = ob->ob.arcellipse.open;
1361 				};
1362 			};
1363 		l = l->next;
1364 		};
1365 
1366 }
1367 
1368 /* calc a mid point bent by angle  */
1369 void
bent_midpoint(long x1,long y1,long x2,long y2,double angle,short * ox,short * oy)1370 bent_midpoint(long x1,long y1,long x2,long y2,double angle, short *ox, short *oy)
1371 {
1372 	long x3,y3;
1373 	double sina,cosa;
1374 
1375 	sina = sin(angle);
1376 	cosa = cos(angle);
1377 
1378 	x3 = (x2-x1)/2;
1379 	y3 = (y2-y1)/2;
1380 
1381 	*ox = x1 + cosa*x3 - sina*y3;
1382 	*oy = y1 + sina*x3 + cosa*y3;
1383 }
1384 
1385 /* snap a value by grid size  */
1386 long
snap(long size,long val)1387 snap(long size, long val)
1388 {
1389 	if ((val % size) > (size/2))
1390 		return ((val/size)+1)*size;
1391 	else
1392 		return (val/size)*size;
1393 }
1394