1 /* text.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 /* contains functions assoc. with text editing and drawing */
18 /*
19  * $Log: text.c,v $
20  * Revision 1.2  2000/12/06 20:56:06  moz
21  * GPL stuff.
22  *
23  * Revision 1.1.1.1  2000/08/21 01:05:31  moz
24  *
25  *
26  * Revision 1.1.1.1  2000/07/19 22:45:31  moz
27  * CVS Import
28  *
29  * Revision 1.7  2000/05/03 22:42:26  moz
30  * Cope better with font being NULL in new_text().
31  *
32  * Revision 1.6  2000/03/13 02:47:07  moz
33  * Fill in useless Object parameters.
34  *
35  * Revision 1.5  2000/03/11 19:11:11  moz
36  * Fixed stupid typos in text_object_at_pixel_point().
37  * Don't adjust text position on load of rotated text.
38  *
39  * Revision 1.4  2000/03/11 18:20:05  moz
40  * Compile fix for previous revision.
41  *
42  * Revision 1.3  2000/03/11 17:59:26  moz
43  * Place properly when changing rotated text size.
44  *
45  * Revision 1.2  2000/03/10 18:01:20  moz
46  * Fix for compile fix.
47  *
48  * Revision 1.1  2000/03/10 17:48:17  moz
49  * Initial revision
50  *
51  * Revision 1.24  2000/03/07 21:05:26  moz
52  * Compile fixes.
53  *
54  * Revision 1.23  2000/03/05 21:18:25  moz
55  * Removed old clipping stuff and tidied up a bit.
56  *
57  * Revision 1.22  2000/03/05 20:42:24  moz
58  * text_object_at_pixel_point() selects only text objects.
59  *
60  * Revision 1.21  2000/03/05 00:07:47  moz
61  * Fix coredump when placing cursor in empty object.
62  *
63  * Revision 1.20  2000/02/18 03:04:32  moz
64  * Free empty text line.
65  *
66  * Revision 1.19  2000/01/28 15:59:45  moz
67  * Hopefully checking isprint() will not allow control chars.
68  *
69  * Revision 1.18  1999/11/15 02:02:28  moz
70  * Use rounded trig.
71  * Don't dump core on cs==NULL.
72  *
73  * Revision 1.17  1999/08/08 20:55:56  moz
74  * From clean up of structs.
75  *
76  * Revision 1.16  1999/07/29 20:44:27  moz
77  * Draw rubber boxes for resizing text.
78  *
79  * Revision 1.15  1999/05/24 01:01:41  moz
80  * Don't create nil-dimension pixmaps.
81  *
82  * Revision 1.14  1999/05/19 17:06:53  moz
83  * 1.0 Checkin.
84  *
85  * Revision 1.13  1999/05/04 15:50:38  moz
86  * Moved list functions to list.c
87  *
88  * Revision 1.12  1999/04/28 16:35:54  moz
89  * fixed bug with calc_text_outer_box.
90  *
91  * Revision 1.11  1999/04/28 16:31:39  moz
92  * Various text bug fixes.
93  *
94  * Revision 1.10  1999/04/28 15:07:07  moz
95  * Reqrite of recalc_text_box.
96  * Fixed two views editing_text problem.
97  *
98  * Revision 1.9  1999/04/27 20:22:30  moz
99  * place_cursor now works for rotated text.
100  *
101  * Revision 1.8  1999/04/27 04:12:12  moz
102  * Rotated cursor added.
103  *
104  * Revision 1.7  1999/04/26 19:22:17  moz
105  * Fixed up drawing of rotated text correctly.
106  *
107  * Revision 1.6  1999/04/25 21:25:26  moz
108  * First pass at rotating text.
109  *
110  * Revision 1.5  1999/04/25 00:21:39  moz
111  * Draw outline when deciding rotation.
112  *
113  * Revision 1.4  1999/04/24 22:23:06  moz
114  * gc_colour needs view parameter.
115  *
116  * Revision 1.3  1999/04/22 22:17:15  moz
117  * Ignore control characters on non-Linux OSs.
118  *
119  * Revision 1.2  1999/04/22 21:26:46  moz
120  * Fix centre and right-justified text entry.
121  *
122  * Revision 1.1  1999/03/30 00:06:58  moz
123  * Initial revision
124  *
125  */
126 
127 /* see mouse_button.c for general behaviour */
128 
129 #include <math.h>
130 #include <X11/keysym.h>
131 #include <string.h>
132 #include <ctype.h>
133 #include "include/figurine.h"
134 #include "include/extern.h"
135 
136 static ListEl *cl=NULL; /* current text line  */
137 static ListEl *cs=NULL; /* current section on line */
138 static Object *mob=NULL;
139 static int cx; /* current position of text cursor  */
140 static int cy;
141 static int charpos; /* in each 19-char section */
142 View *editview=NULL; /* which view are we editing in */
143 
144 /* note: do NOT use with nodes */
145 /* with text, ob->bbox describes the furthest
146 	reaches of the text. when rotated, ob->ob.text.bbox
147 	is kept, and ob->bbox is calculated by rotating the
148 	points. The final position is adjusted by dx,dy */
149 void
calc_text_outer_box_adjust(Object * ob,long dx,long dy)150 calc_text_outer_box_adjust(Object *ob, long dx, long dy)
151 {
152 	if (ob->ob.text.angle==0.0)
153 		{
154 		ob->bbox.x1 = ob->ob.text.bbox.x1;
155 		ob->bbox.y1 = ob->ob.text.bbox.y1;
156 		ob->bbox.x2 = ob->ob.text.bbox.x2;
157 		ob->bbox.y2 = ob->ob.text.bbox.y2;
158 		}
159 	else
160 		{
161 		VRegion a1,a2,a3,a4;
162 		double sina,cosa;
163 		long ccx,ccy;
164 		sina = sinround(ob->ob.text.angle);
165 		cosa = cosround(ob->ob.text.angle);
166 		ccx = (ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)/2 + ob->ob.text.bbox.x1;
167 		ccy = (ob->ob.text.bbox.y2-ob->ob.text.bbox.y1)/2 + ob->ob.text.bbox.y1;
168 		a1.x1 = ccx + (cosa*(ob->ob.text.bbox.x1-ccx))-(sina*(ob->ob.text.bbox.y1-ccy));
169 		a1.y1 = ccy + (sina*(ob->ob.text.bbox.x1-ccx))+(cosa*(ob->ob.text.bbox.y1-ccy));
170 		a1.x2 = ccx + (cosa*(ob->ob.text.bbox.x2-ccx))-(sina*(ob->ob.text.bbox.y1-ccy));
171 		a1.y2 = ccy + (sina*(ob->ob.text.bbox.x2-ccx))+(cosa*(ob->ob.text.bbox.y1-ccy));
172 		a2.x1 = ccx + (cosa*(ob->ob.text.bbox.x2-ccx))-(sina*(ob->ob.text.bbox.y1-ccy));
173 		a2.y1 = ccy + (sina*(ob->ob.text.bbox.x2-ccx))+(cosa*(ob->ob.text.bbox.y1-ccy));
174 		a2.x2 = ccx + (cosa*(ob->ob.text.bbox.x2-ccx))-(sina*(ob->ob.text.bbox.y2-ccy));
175 		a2.y2 = ccy + (sina*(ob->ob.text.bbox.x2-ccx))+(cosa*(ob->ob.text.bbox.y2-ccy));
176 		a3.x1 = ccx + (cosa*(ob->ob.text.bbox.x2-ccx))-(sina*(ob->ob.text.bbox.y2-ccy));
177 		a3.y1 = ccy + (sina*(ob->ob.text.bbox.x2-ccx))+(cosa*(ob->ob.text.bbox.y2-ccy));
178 		a3.x2 = ccx + (cosa*(ob->ob.text.bbox.x1-ccx))-(sina*(ob->ob.text.bbox.y2-ccy));
179 		a3.y2 = ccy + (sina*(ob->ob.text.bbox.x1-ccx))+(cosa*(ob->ob.text.bbox.y2-ccy));
180 		a4.x1 = ccx + (cosa*(ob->ob.text.bbox.x1-ccx))-(sina*(ob->ob.text.bbox.y2-ccy));
181 		a4.y1 = ccy + (sina*(ob->ob.text.bbox.x1-ccx))+(cosa*(ob->ob.text.bbox.y2-ccy));
182 		a4.x2 = ccx + (cosa*(ob->ob.text.bbox.x1-ccx))-(sina*(ob->ob.text.bbox.y1-ccy));
183 		a4.y2 = ccy + (sina*(ob->ob.text.bbox.x1-ccx))+(cosa*(ob->ob.text.bbox.y1-ccy));
184 		normalise_rectangle(&a1.x1,&a1.y1,&a1.x2,&a1.y2);
185 		normalise_rectangle(&a2.x1,&a2.y1,&a2.x2,&a2.y2);
186 		normalise_rectangle(&a1.x1,&a1.y1,&a1.x2,&a1.y2);
187 		normalise_rectangle(&a2.x1,&a2.y1,&a2.x2,&a2.y2);
188 		normalise_rectangle(&a3.x1,&a3.y1,&a3.x2,&a3.y2);
189 		normalise_rectangle(&a4.x1,&a4.y1,&a4.x2,&a4.y2);
190 		ob->bbox = merge_boxes(a1,merge_boxes(a2,merge_boxes(a3,a4)));
191 		ob->bbox.x1 -= dx;
192 		ob->bbox.y1 -= dy;
193 		ob->bbox.x2 -= dx;
194 		ob->bbox.y2 -= dy;
195 		ob->ob.text.bbox.x1 -= dx;
196 		ob->ob.text.bbox.y1 -= dy;
197 		ob->ob.text.bbox.x2 -= dx;
198 		ob->ob.text.bbox.y2 -= dy;
199 		};
200 }
201 
202 void
set_text_just(View * view)203 set_text_just(View *view)
204 {
205 	if (mob!=NULL && cl!=NULL)
206 		{
207 		TEXTLINE(cl)->just = view->justification;
208 		recalc_text_box(view,mob,FALSE,FALSE);
209 		};
210 }
211 
212 void
unselect_text(View * view)213 unselect_text(View *view)
214 {
215 	state.editing_text = FALSE;
216 	view->text_object = NULL;
217 	cl = NULL; cs = NULL;
218 
219 	if (mob!=NULL && TEXTLINE(mob->ob.text.lines)->sections==NULL)
220 		{
221 		view->doc->o = trash_object(view->doc->o, &view->doc->lo, mob);
222 		send_redraw_object(view,mob);
223 		if (mob->ob.text.ellipse!=NULL)
224 			free(mob->ob.text.ellipse);
225 		free(TEXTLINE(mob->ob.text.lines));
226 		free(mob->ob.text.lines);
227 		free(mob);
228 		}
229 	else if (mob!=NULL)
230 		send_redraw_object(view,mob);
231 	mob = NULL;
232 	charpos = 0;
233 	cx = 0;
234 	cy = 0;
235 }
236 
237 /* place the text cursor at document position  */
238 /* x,y must be relative to ob.text.bbox, in DOCUMENT CO-ORDS  */
239 void
place_cursor(View * view,int x,int y)240 place_cursor(View *view, int x, int y)
241 {
242 	List l,l2,ol=NULL;
243 	uint i=0;
244 	VFont *vf;
245 
246 	/* rotate place point back if necessary  */
247 	if (mob->ob.text.angle!=0.0)
248 		{
249 		double sina,cosa;
250 		long ccx,ccy;
251 		long tx;
252 
253 		sina = sinround(-mob->ob.text.angle);
254 		cosa = cosround(-mob->ob.text.angle);
255 		ccx = (mob->ob.text.bbox.x2-mob->ob.text.bbox.x1)/2;
256 		ccy = (mob->ob.text.bbox.y2-mob->ob.text.bbox.y1)/2;
257 
258 		tx = ccx + (cosa*(x-ccx))-(sina*(y-ccy));
259 		y = ccy + (sina*(x-ccx))+(cosa*(y-ccy));
260 		x = tx;
261 		};
262 
263 	l = mob->ob.text.lines;
264 
265 	while (l!=NULL)
266 		{
267 		if (y>=TEXTLINE(l)->y && y <=(TEXTLINE(l)->y+TEXTLINE(l)->h))
268 			{
269 			l2 = TEXTLINE(l)->sections;
270 			while (l2!=NULL)
271 				{
272 				if (x>=TEXTSEC(l2)->x && x <=(TEXTSEC(l2)->x+TEXTSEC(l2)->w))
273 					{
274 					vf = get_font(TEXTSEC(l2)->num,ZPO(TEXTSEC(l2)->size,view));
275 					if (vf==NULL)
276 						return;
277 					while (x-TEXTSEC(l2)->x > P2D(XTextWidth(vf->x,TEXTSEC(l2)->text,(int)i),view) && i<=strlen(TEXTSEC(l2)->text))
278 						i++;
279 					charpos = i;
280 					cs = l2;
281 					cl = l;
282 					cx = TEXTSEC(l2)->x + P2D(XTextWidth(vf->x,TEXTSEC(l2)->text,(int)i),view);
283 					cy = TEXTLINE(l)->y;
284 					break;
285 					};
286 				ol = l2;
287 				l2 = l2->next;
288 				};
289 
290 			/* new empty string */
291 			if (!ol)
292 				return;
293 
294 			if (l2==NULL && x <= mob->ob.text.bbox.x2 - mob->ob.text.bbox.x1)
295 				{
296 				vf = get_font(TEXTSEC(ol)->num,ZPO(TEXTSEC(ol)->size,view));
297 				if (vf==NULL)
298 					return;
299 				charpos = strlen(TEXTSEC(ol)->text);
300 				cs = ol;
301 				cl = l;
302 				cx = TEXTSEC(ol)->x + P2D(XTextWidth(vf->x,TEXTSEC(ol)->text,(int)strlen(TEXTSEC(ol)->text)),view);
303 				cy = TEXTLINE(l)->y;
304 				};
305 			break;
306 			};
307 		l = l->next;
308 		};
309 }
310 
311 /* create a new text object  */
312 void
new_text(BEvent * bev,View * view)313 new_text(BEvent *bev, View *view)
314 {
315 	VFont *vf;
316 	TextLine *tl;
317 	VRegion bb;
318 
319 	/* start a new object */
320 	state.editing_text=TRUE;
321 	mob = (Object *)malloc(sizeof(Object));
322 
323 	mob->type = TEXT;
324 	mob->ticket = ob_ticket++;
325 	mob->derries = NULL;
326 	mob->depth = view->doc->ob_depth--;
327 	mob->colour = view->colour;
328 	mob->fillcolour = view->fillcolour;
329 	mob->ls = view->linestyle;
330 	mob->lw = view->linewidth;
331 	mob->fs = view->fillstyle;
332 	mob->es = view->endstyle;
333 	mob->js = view->endstyle;
334 	mob->farrow = NULL;
335 	mob->barrow = NULL;
336 	mob->ob.text.node = (state.current_icon==NODEICON);
337 	mob->ob.text.ellipse = NULL;
338 	mob->ob.text.angle = 0.0;
339 
340 	bb = mob->bbox;
341 
342 	cx = cy = 0;
343 	tl = (TextLine *)malloc(sizeof(TextLine));
344 	tl->just = view->justification;
345 	mob->ob.text.lines = insert_before(NULL,NULL, (ulong)cy, 0, (void *)tl, &cl);
346 	TEXTLINE(cl)->y = 0;
347 	TEXTLINE(cl)->w = 0;
348 	TEXTLINE(cl)->sections = NULL;
349 
350 	vf = get_font(view->fontnum, ZPO(view->fontsize,view));
351 
352 	/* give up */
353 	if (vf==NULL)
354 		{
355 		state.editing_text = FALSE;
356 		view->text_object = NULL;
357 		cl = NULL; cs = NULL;
358 		charpos=cx=cy=0;
359 		free(tl);
360 		free(mob->ob.text.lines);
361 	       	free(mob);
362 		return;
363 		};
364 
365 	if (view->gridon)
366 		{
367 		mob->ob.text.bbox.x1 = GXP2D(bev->x,view) - P2D(2,view);
368 		mob->ob.text.bbox.y1 = GYP2D(bev->y - vf->x->max_bounds.ascent,view);
369 		}
370 	else
371 		{
372 		mob->ob.text.bbox.x1 = XP2D(bev->x,view) - P2D(2,view);
373 		mob->ob.text.bbox.y1 = YP2D(bev->y - vf->x->max_bounds.ascent,view);
374 		};
375 
376 	recalc_text_box(view,mob,FALSE,FALSE); /* also makes ellipse */
377 	view->doc->o = add_object(view->doc->o, &view->doc->lo, mob);
378 	register_undo(UNDO_PASTE,mob,view->doc);
379 	view->text_object = mob;
380 	send_redraw_object(view,mob);
381 }
382 
383 Object *text_object_at_pixel_point(View *view, int x, int y);
384 
385 Object *
text_object_at_pixel_point(View * view,int x,int y)386 text_object_at_pixel_point(View *view, int x, int y)
387 {
388 	List l,l2;
389 	ulong depth=ULONG_MAX;
390 	Object *foundob=NULL;
391 
392 	l = intersecting_objects_point(view->doc->o, XP2D(x,view), YP2D(y,view), NULL);
393 	l2 = l;
394 	while (l2!=NULL)
395 		{
396 		if (OB(l2)->type==TEXT && OB(l2)->depth<=depth)
397 			{
398 			depth = OB(l2)->depth;
399 			foundob = OB(l2);
400 			};
401 		l2=l2->next;
402 		};
403 
404 	delete_list(l);
405 
406 	return foundob;
407 }
408 
409 void
text_button(BEvent * bev,View * view)410 text_button(BEvent *bev, View *view)
411 {
412 
413 	if (bev->button==Button1 && (bev->type==BUTTON_RELEASED || bev->type==BUTTON_CLICKED) &&
414 	   (!state.editing_text || editview!=view))
415 		{
416 		Object *fob;
417 
418 		fob = text_object_at_pixel_point(view,bev->x,bev->y);
419 
420 		if (view!=editview)
421 			{
422 			if (editview!=NULL)
423 				unselect_text(editview);
424 			editview = view;
425 			};
426 
427 		if (fob)
428 			{
429 			mob = fob;
430 			state.editing_text = TRUE;
431 			editview = view;
432 			view->text_object = mob;
433 			place_cursor(view,XP2D(bev->x,view)-mob->ob.text.bbox.x1,YP2D(bev->y,view)-mob->ob.text.bbox.y1);
434 			/* ensure the cursor's there  */
435 			if (cl==NULL)
436 				{
437 				cl = mob->ob.text.lines;
438 				cs = TEXTLINE(cl)->sections;
439 				charpos = 0;
440 				cx = cy = 0;
441 				};
442 			send_redraw_object(view,mob);
443 			}
444 		else
445 			{
446 			new_text(bev,view);
447 			editview=view;
448 			};
449 		}
450 	else if (state.editing_text && (bev->type==BUTTON_RELEASED || bev->type==BUTTON_CLICKED)
451 				&& bev->button!=Button2)
452 		{
453 		if (!is_in_bbox(bev->x, bev->y, XD2P(mob->ob.text.bbox.x1,view),
454 			 YD2P(mob->ob.text.bbox.y1,view), XD2P(mob->ob.text.bbox.x2,view), YD2P(mob->ob.text.bbox.y2,view)))
455 			{
456 			Object *fob;
457 
458 			unselect_text(view);
459 			if (state.current_icon==ARCICON)
460 				unselect_arc(view);
461 
462 			fob = text_object_at_pixel_point(view,bev->x,bev->y);
463 
464 			if (fob)
465 				{
466 				mob = fob;
467 				state.editing_text = TRUE;
468 				view->text_object = mob;
469 				place_cursor(view,XP2D(bev->x,view)-mob->ob.text.bbox.x1,YP2D(bev->y,view)-mob->ob.text.bbox.y1);
470 				/* ensure the cursor's there  */
471 				if (cl==NULL)
472 					{
473 					cl = mob->ob.text.lines;
474 					cs = TEXTLINE(cl)->sections;
475 					charpos = 0;
476 					cx = cy = 0;
477 					};
478 				send_redraw_object(view,mob);
479 				}
480 			else if (state.current_icon==TEXTICON && bev->button!=Button3)
481 				new_text(bev,view);
482 			}
483 		else
484 			{
485 			place_cursor(view,XP2D(bev->x,view)-mob->ob.text.bbox.x1,YP2D(bev->y,view)-mob->ob.text.bbox.y1);
486 			/* ensure the cursor's there  */
487 			if (cl==NULL)
488 				{
489 				cl = mob->ob.text.lines;
490 				cs = TEXTLINE(cl)->sections;
491 				charpos = 0;
492 				cx = cy = 0;
493 				};
494 			send_redraw_object(view,mob);
495 			};
496 		};
497 }
498 
499 /* called by zoom.c - we need accurate metrics on font
500 	at current zoom. This doesn't affect the stored text.bbox size
501 	(zoom==FALSE) */
502 void
recalc_text_boxes(View * view)503 recalc_text_boxes(View *view)
504 {
505 	List l;
506 
507 	l = view->doc->lo;
508 
509 	while (l!=NULL)
510 		{
511 		if (OB(l)->type==TEXT)
512 			recalc_text_box(view,OB(l),TRUE,FALSE);
513 		l = l->next;
514 		};
515 }
516 
517 /* recalculate the size of the ob->ob.text.bbox  */
518 void
recalc_text_box(View * view,Object * ob,Boolean zoom,Boolean load)519 recalc_text_box(View *view, Object *ob, Boolean zoom, Boolean load)
520 {
521 	List l;
522 	List l2;
523 	long x=0,y=0,dx=0,ox=0,oy=0;
524 	long w=0;
525 	long maxw=0,maxh=0;
526 	VFont *vf;
527 
528 	if (ob->ob.text.angle!=0.0)
529 		{
530 		double sina,cosa;
531 		long ccx,ccy;
532 		sina = sinround(ob->ob.text.angle);
533 		cosa = cosround(ob->ob.text.angle);
534 		ccx = (ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)/2 + ob->ob.text.bbox.x1;
535 		ccy = (ob->ob.text.bbox.y2-ob->ob.text.bbox.y1)/2 + ob->ob.text.bbox.y1;
536 		ox = ccx + (cosa*(ob->ob.text.bbox.x1-ccx))-(sina*(ob->ob.text.bbox.y1-ccy));
537 		oy = ccy + (sina*(ob->ob.text.bbox.x1-ccx))+(cosa*(ob->ob.text.bbox.y1-ccy));
538 		};
539 
540 	l = ob->ob.text.lines;
541 
542 	while (l!=NULL)
543 		{
544 		x=0;w=0;maxh=0;
545 
546 		l2 = TEXTLINE(l)->sections;
547 		TEXTLINE(l)->y = y;
548 
549 		while (l2!=NULL)
550 			{
551 			vf = get_font(TEXTSEC(l2)->num, ZPO(TEXTSEC(l2)->size,view));
552 			if (vf!=NULL)
553 				{
554 				TEXTSEC(l2)->x = w;
555 				TEXTSEC(l2)->w = P2D(XTextWidth(vf->x, TEXTSEC(l2)->text, (int)strlen(TEXTSEC(l2)->text)),view);
556 				w += TEXTSEC(l2)->w;
557 				if (strlen(TEXTSEC(l2)->text))
558 					maxh=max(maxh,P2D(vf->x->max_bounds.ascent+vf->x->max_bounds.descent,view));
559 				}
560 			else
561 				{
562 				TEXTSEC(l2)->x = w;
563 				w += TEXTSEC(l2)->w;
564 				};
565 			l2 = l2->next;
566 			};
567 
568 		TEXTLINE(l)->w = w;
569 		TEXTLINE(l)->h = maxh;
570 		y += TEXTLINE(l)->h;
571 		maxw = max(maxw,w);
572 
573 		l = l->next;
574 		};
575 
576 	if (!zoom)
577 		{
578 		/* calculate lowerright corner  */
579 		vf = get_font(view->fontnum, ZPO(view->fontsize,view));
580 		ob->ob.text.bbox.x2 = ob->ob.text.bbox.x1 + max(P2D(3,view),maxw)+5;
581 		if (vf!=NULL)
582 			{
583 			if (y==0)
584 				{
585 				if (TEXTLINE(ob->ob.text.lines)->sections!=NULL &&
586 				    !strlen(TEXTSEC(TEXTLINE(ob->ob.text.lines)->sections)->text))
587 					{
588 					TextSection *tts=TEXTSEC(TEXTLINE(ob->ob.text.lines)->sections);
589 					vf = get_font(tts->num, ZPO(tts->size,view));
590 					};
591 				ob->ob.text.bbox.y2 = ob->ob.text.bbox.y1 +
592 				                      P2D(vf->x->max_bounds.ascent+vf->x->max_bounds.descent,view);
593 				}
594 			else
595 				ob->ob.text.bbox.y2 = ob->ob.text.bbox.y1 + y;
596 			}
597 		else
598 			ob->ob.text.bbox.y2 = ob->ob.text.bbox.y1 + y;
599 		};
600 
601 	cx = 0;
602 	if (cs!=NULL)
603 		{
604 		vf = get_font(TEXTSEC(cs)->num, ZPO(TEXTSEC(cs)->size,view));
605 		if (vf!=NULL)
606 			cx = TEXTSEC(cs)->x + P2D(XTextWidth(vf->x,TEXTSEC(cs)->text,charpos),view);
607 		};
608 
609 	/* handle justification  */
610 	l = ob->ob.text.lines;
611 	while (l!=NULL)
612 		{
613 		if (state.editing_text && mob==ob && cl==l)
614 			cy = TEXTLINE(l)->y;
615 
616 		switch (TEXTLINE(l)->just)
617 			{
618 			case LEFT:
619 				dx = 0;
620 				break;
621 			case CENTRE:
622 				dx = ((ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)-TEXTLINE(l)->w)/2;
623 				break;
624 			case RIGHT:
625 				dx = (ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)-TEXTLINE(l)->w;
626 				break;
627 			};
628 
629 		l2 = TEXTLINE(l)->sections;
630 		while (l2!=NULL)
631 			{
632 			TEXTSEC(l2)->x += dx;
633 			if (l2==cs)
634 				cx += dx;
635 			l2 = l2->next;
636 			};
637 
638 		l = l->next;
639 		};
640 
641 	if (!zoom)
642 		{
643 		if (ob->ob.text.node)
644 			{
645 			Object *e=ob->ob.text.ellipse;
646 			/* create/update ellipse  */
647 			if (ob->ob.text.ellipse==NULL)
648 				{
649 				ob->ob.text.ellipse = (Object *)malloc(sizeof(Object));
650 				e=ob->ob.text.ellipse;
651 				e->type = ELLIPSE;
652 				e->ticket = ob_ticket++;
653 				e->derries = NULL;
654 				e->depth = ob->depth+1;
655 				e->ls = view->linestyle;
656 				e->es = view->endstyle;
657 				e->js = JoinRound;
658 				e->lw = view->linewidth;
659 				e->colour = view->colour;
660 				e->fillcolour = view->fillcolour;
661 				e->farrow = NULL;
662 				e->barrow = NULL;
663 				if (view->fillon)
664 					e->fs = view->fillstyle;
665 				else
666 					e->fs = NONE;
667 
668 				e->dash = 5;
669 				e->gap = 4;
670 				};
671 
672 			ob->bbox.x1 = ob->ob.text.bbox.x1 - (ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)/2;
673 			ob->bbox.y1 = ob->ob.text.bbox.y1 - (ob->ob.text.bbox.y2-ob->ob.text.bbox.y1)/2;
674 			ob->bbox.x2 = ob->ob.text.bbox.x1 + 3*(ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)/2;
675 			ob->bbox.y2 = ob->ob.text.bbox.y1 + 3*(ob->ob.text.bbox.y2-ob->ob.text.bbox.y1)/2;
676 			e->ob.ellipse.centre.x = (ob->bbox.x2-ob->bbox.x1)/2;
677 			e->ob.ellipse.centre.y = (ob->bbox.y2-ob->bbox.y1)/2;
678 			e->ob.ellipse.xradius = (ob->bbox.x2-ob->bbox.x1)/2;
679 			e->ob.ellipse.yradius = (ob->bbox.y2-ob->bbox.y1)/2;
680 			}
681 		else
682 			{
683 			if (load || ob->ob.text.angle==0.0)
684 				calc_text_outer_box_adjust(ob,0,0);
685 			else
686 				{
687 				double sina,cosa;
688 				long ccx,ccy;
689 				long ddx,ddy;
690 				sina = sinround(ob->ob.text.angle);
691 				cosa = cosround(ob->ob.text.angle);
692 				ccx = (ob->ob.text.bbox.x2-ob->ob.text.bbox.x1)/2 + ob->ob.text.bbox.x1;
693 				ccy = (ob->ob.text.bbox.y2-ob->ob.text.bbox.y1)/2 + ob->ob.text.bbox.y1;
694 				ddx = ccx + ((long)((cosa*(ob->ob.text.bbox.x1-ccx))-(sina*(ob->ob.text.bbox.y1-ccy)))) - ox;
695 				ddy = ccy + ((long)((sina*(ob->ob.text.bbox.x1-ccx))+(cosa*(ob->ob.text.bbox.y1-ccy)))) - oy;
696 				calc_text_outer_box_adjust(ob, ddx, ddy);
697 				};
698 			};
699 
700 		resize_bbox(view->doc->o,ob);
701 		};
702 }
703 
704 /* insert a char into a string str  */
705 void
insert_char(char * str,char c,int pos)706 insert_char(char *str, char c, int pos)
707 {
708 	char a[20]="";
709 
710 	strncpy(a,str,(uint)pos);
711 	a[strlen(a)+1]='\0';
712 	a[strlen(a)]=c;
713 	strcat(a,&str[pos]);
714 	strcpy(str,a);
715 }
716 
717 /* create a new text section  */
718 /* text is split into TextLines; each
719 	Line has one or more TextSections with
720 	different fonts and text */
721 TextSection *
new_section(View * view)722 new_section(View *view)
723 {
724 	TextSection *ts;
725 
726 	ts = (TextSection *)malloc(sizeof(TextSection));
727 
728 	ts->isReturn = FALSE;
729 	ts->num = view->fontnum;
730 	ts->size = view->fontsize;
731 	ts->x = cx;
732 	ts->w = 0;
733 	strcpy(ts->text,"");
734 	return ts;
735 }
736 
737 /* add a char at the current position  */
738 /* must take care in end-of-line situations
739 	and not to overflow one textsection string buffer */
740 void
add_char(View * view,char * c)741 add_char(View *view, char *c)
742 {
743 	TextSection *ts;
744 
745 	if (cs==NULL)
746 		{
747 		/* only when no sections on line */
748 		ts = new_section(view);
749 		TEXTLINE(cl)->sections = add_to_list(TEXTLINE(cl)->sections,0,0,(void *)ts);
750 		cs = TEXTLINE(cl)->sections;
751 		};
752 
753 	if ((view->fontnum!=TEXTSEC(cs)->num || view->fontsize!=TEXTSEC(cs)->size)
754 		 && charpos==(int)strlen(TEXTSEC(cs)->text) && (cs->next==NULL || streq(TEXTSEC(cs->next)->text,"")))
755 		{
756 		if (charpos==0)
757 			{
758 			send_redraw_object(view,mob);
759 			TEXTSEC(cs)->size = view->fontsize;
760 			TEXTSEC(cs)->num = view->fontnum;
761 			}
762 		else
763 			{
764 			ts = new_section(view);
765 			if (cs->next!=NULL)
766 				{
767 				ts->size = TEXTSEC(cs)->size;
768 				ts->num = TEXTSEC(cs)->num;
769 				};
770 			TEXTLINE(cl)->sections = insert_after(TEXTLINE(cl)->sections,cs,0,0,(void *)ts, &cs);
771 			};
772 		insert_char(TEXTSEC(cs)->text,*c,0);
773 		charpos = 1;
774 		}
775 	else if (strlen(TEXTSEC(cs)->text)<19)
776 		{
777 		insert_char(TEXTSEC(cs)->text,*c,charpos);
778 		charpos++;
779 		}
780 	else
781 		{
782 		/* doesn't fit in current textsection, create new */
783 		if (charpos == 19)
784 			{
785 			/* at end */
786 			ts = new_section(view);
787 			if (cs->next!=NULL)
788 				{
789 				ts->size = TEXTSEC(cs)->size;
790 				ts->num = TEXTSEC(cs)->num;
791 				};
792 			TEXTLINE(cl)->sections = insert_after(TEXTLINE(cl)->sections,cs,0,0,(void *)ts, &cs);
793 			charpos = 0;
794 			insert_char(TEXTSEC(cs)->text,*c,charpos++);
795 			}
796 		else if (charpos == 0)
797 			{
798 			/* before cs */
799 			ts = new_section(view);
800 			if (cs->next!=NULL)
801 				{
802 				ts->size = TEXTSEC(cs)->size;
803 				ts->num = TEXTSEC(cs)->num;
804 				};
805 			TEXTLINE(cl)->sections = insert_before(TEXTLINE(cl)->sections,cs,0,0,(void *)ts, &cs);
806 			charpos = 0;
807 			insert_char(TEXTSEC(cs)->text,*c,charpos++);
808 			}
809 		else
810 			{
811 			/* inbetween must split */
812 			ts = new_section(view);
813 			if (cs->next!=NULL)
814 				{
815 				ts->size = TEXTSEC(cs)->size;
816 				ts->num = TEXTSEC(cs)->num;
817 				};
818 			strcpy(ts->text,&TEXTSEC(cs)->text[charpos]);
819 			TEXTSEC(cs)->text[charpos] = '\0';
820 			TEXTLINE(cl)->sections = insert_after(TEXTLINE(cl)->sections,cs,0,0,(void *)ts, &cs);
821 			insert_char(TEXTSEC(cs)->text,*c,0);
822 			charpos = 1;
823 			};
824 		};
825 }
826 
827 /* handle a keypress  */
828 void
text_key(View * view,XKeyPressedEvent * report)829 text_key(View *view, XKeyPressedEvent *report)
830 {
831 	KeySym keysym;
832 	char str[50]="";
833 	int num=0;
834 	VFont *vf;
835 
836 	num = XLookupString(report,str,sizeof(str), &keysym, NULL);
837 	str[num]='\0';
838 
839 	if (mob==NULL)
840 		return;
841 
842 	if (cs!=NULL)
843 		vf = get_font(TEXTSEC(cs)->num, ZPO(TEXTSEC(cs)->size,view));
844 	else
845 		vf = get_font(view->fontnum, ZPO(view->fontsize,view));
846 
847 	if (vf==NULL)
848 		return;
849 
850 	switch (keysym)
851 		{
852 		/* handle creation of a new line  */
853 		case XK_Return:
854 		case XK_KP_Enter:
855 			if (cs==NULL || charpos==(int)strlen(TEXTSEC(cs)->text))
856 				{
857 				TextLine *tl;
858 				TextSection *ts;
859 
860 				tl = (TextLine *)malloc(sizeof(TextLine));
861 				tl->just = view->justification;
862 				mob->ob.text.lines = insert_after(mob->ob.text.lines,cl,0,0,(void *)tl, &cl);
863 				ts = new_section(view);
864 				if (cs==NULL)
865 					{
866 					TEXTLINE(cl)->sections = add_to_list(NULL, 0, 0, (void *)ts);
867 					cs=TEXTLINE(cl)->sections;
868 					}
869 				else
870 					{
871 					ts->size = TEXTSEC(cs)->size;
872 					ts->num = TEXTSEC(cs)->num;
873 					TEXTLINE(cl)->sections = insert_after(NULL,NULL,0,0,(void *)ts, &cs);
874 					};
875 
876 				charpos = 0;
877 				store_redraw_object(mob);
878 				recalc_text_box(view,mob,FALSE,FALSE);
879 				view->doc->o = change_bbox(view->doc->o, mob);
880 				send_stored_redraw_object(view,mob);
881 				}
882 			else
883 				{
884 				/* must split */
885 				TextLine *tl;
886 				TextSection *ts;
887 				ListEl *le;
888 				VRegion bb;
889 
890 				store_redraw_object(mob);
891 				tl = (TextLine *)malloc(sizeof(TextLine));
892 				tl->just = view->justification;
893 				mob->ob.text.lines = insert_after(mob->ob.text.lines,cl,0,0,(void *)tl, &cl);
894 				ts = new_section(view);
895 				ts->size = TEXTSEC(cs)->size;
896 				ts->num = TEXTSEC(cs)->num;
897 				strcpy(ts->text,&TEXTSEC(cs)->text[charpos]);
898 				TEXTSEC(cs)->text[charpos] = '\0';
899 				le = cs->next;
900 				cs->next = NULL;
901 				TEXTLINE(cl)->sections = insert_after(NULL,NULL,0,0,(void *)ts, &cs);
902 				cs->prev = NULL;
903 				cs->next = le;
904 				if (le!=NULL)
905 					le->prev = cs;
906 				charpos = 0;
907 				bb = mob->bbox;
908 					recalc_text_box(view,mob,FALSE,FALSE);
909 				view->doc->o = change_bbox(view->doc->o ,mob);
910 				send_stored_redraw_object(view,mob);
911 				};
912 			break;
913 
914 		case XK_Left:
915 			if (cs!=NULL)
916 				{
917 				if (charpos>0)
918 					{
919 					charpos--;
920 					vf = get_font(TEXTSEC(cs)->num, ZPO(TEXTSEC(cs)->size,view));
921 					cx = TEXTSEC(cs)->x + P2D(XTextWidth(vf->x, TEXTSEC(cs)->text, charpos),view);
922 					send_redraw_object(view,mob);
923 					}
924 				else if (cs->prev!=NULL)
925 					{
926 					cs = cs->prev;
927 					charpos = max(0,((int)strlen(TEXTSEC(cs)->text))-1);
928 					vf = get_font(TEXTSEC(cs)->num, ZPO(TEXTSEC(cs)->size,view));
929 					cx = TEXTSEC(cs)->x + P2D(XTextWidth(vf->x, TEXTSEC(cs)->text, charpos),view);
930 					send_redraw_object(view,mob);
931 					}
932 				else if (cl->prev!=NULL)
933 					{
934 					List l,pl=NULL;
935 
936 					cl = cl->prev;
937 					l = TEXTLINE(cl)->sections;
938 
939 					while (l!=NULL)
940 						{
941 						pl = l;
942 						l = l->next;
943 						};
944 
945 					cs = pl;
946 					charpos = strlen(TEXTSEC(cs)->text);
947 					vf = get_font(TEXTSEC(cs)->num, ZPO(TEXTSEC(cs)->size,view));
948 					cx = TEXTSEC(cs)->x + P2D(XTextWidth(vf->x, TEXTSEC(cs)->text, charpos),view);
949 					cy = TEXTLINE(cl)->y;
950 					send_redraw_object(view,mob);
951 					};
952 				};
953 			break;
954 
955 		case XK_Right:
956 			if (cs!=NULL)
957 				{
958 				if (charpos<19 && (int)strlen(TEXTSEC(cs)->text)>charpos)
959 					{
960 					charpos++;
961 					vf = get_font(TEXTSEC(cs)->num, ZPO(TEXTSEC(cs)->size,view));
962 					cx = TEXTSEC(cs)->x + P2D(XTextWidth(vf->x, TEXTSEC(cs)->text, charpos),view);
963 					send_redraw_object(view,mob);
964 					}
965 				else if (cs->next!=NULL)
966 					{
967 					cs = cs->next;
968 					charpos = min(strlen(TEXTSEC(cs)->text),1);
969 					vf = get_font(TEXTSEC(cs)->num, ZPO(TEXTSEC(cs)->size,view));
970 					cx = TEXTSEC(cs)->x + P2D(XTextWidth(vf->x,TEXTSEC(cs)->text,charpos),view);
971 					send_redraw_object(view,mob);
972 					}
973 				else if (cl->next!=NULL)
974 					{
975 					cl = cl->next;
976 					cs = TEXTLINE(cl)->sections;
977 
978 					charpos = 0;
979 					cx = TEXTSEC(cs)->x;
980 					cy = TEXTLINE(cl)->y;
981 					send_redraw_object(view,mob);
982 					};
983 				};
984 			break;
985 
986 		case XK_Up:
987 			if (cl->prev!=NULL)
988 				{
989 				if (mob->ob.text.angle!=0.0)
990 					{
991 					double sina,cosa;
992 					long crx,cry;
993 					long tx,ty;
994 					sina = sinround(mob->ob.text.angle);
995 					cosa = cosround(mob->ob.text.angle);
996 					crx = (mob->ob.text.bbox.x2-mob->ob.text.bbox.x1)/2;
997 					cry = (mob->ob.text.bbox.y2-mob->ob.text.bbox.y1)/2;
998 					tx = crx + (cosa*(cx-crx))-(sina*(TEXTLINE(cl->prev)->y+3-cry));
999 					ty = cry + (sina*(cx-crx))+(cosa*(TEXTLINE(cl->prev)->y+3-cry));
1000 					place_cursor(view,tx,ty);
1001 					}
1002 				else
1003 					place_cursor(view,cx,TEXTLINE(cl->prev)->y+3);
1004 				send_redraw_object(view,mob);
1005 				};
1006 			break;
1007 
1008 		case XK_Down:
1009 			if (cl->next!=NULL)
1010 				{
1011 				if (mob->ob.text.angle!=0.0)
1012 					{
1013 					double sina,cosa;
1014 					long crx,cry;
1015 					long tx,ty;
1016 					sina = sinround(mob->ob.text.angle);
1017 					cosa = cosround(mob->ob.text.angle);
1018 					crx = (mob->ob.text.bbox.x2-mob->ob.text.bbox.x1)/2;
1019 					cry = (mob->ob.text.bbox.y2-mob->ob.text.bbox.y1)/2;
1020 					tx = crx + (cosa*(cx-crx))-(sina*(TEXTLINE(cl->next)->y+3-cry));
1021 					ty = cry + (sina*(cx-crx))+(cosa*(TEXTLINE(cl->next)->y+3-cry));
1022 					place_cursor(view,tx,ty);
1023 					}
1024 				else
1025 					place_cursor(view,cx,TEXTLINE(cl->next)->y+3);
1026 				send_redraw_object(view,mob);
1027 				};
1028 			break;
1029 
1030 		case XK_BackSpace:
1031 			if (cs!=NULL)
1032 				{
1033 				if (charpos>0)
1034 					{
1035 					VRegion bb;
1036 
1037 					store_redraw_object(mob);
1038 					strcpy(&TEXTSEC(cs)->text[charpos-1],&TEXTSEC(cs)->text[charpos]);
1039 					charpos--;
1040 					bb = mob->bbox;
1041 					recalc_text_box(view,mob,FALSE,FALSE);
1042 					view->doc->o = change_bbox(view->doc->o ,mob);
1043 					send_stored_redraw_object(view,mob);
1044 					}
1045 				else if (cs->prev!=NULL)
1046 					{
1047 					VRegion bb;
1048 					int i;
1049 
1050 					store_redraw_object(mob);
1051 					cs = cs->prev;
1052 					bb = mob->bbox;
1053 					i = strlen(TEXTSEC(cs)->text)-1;
1054 					charpos = max(i,0);
1055 					TEXTSEC(cs)->text[charpos]='\0';
1056 					recalc_text_box(view,mob,FALSE,FALSE);
1057 					view->doc->o = change_bbox(view->doc->o ,mob);
1058 					send_stored_redraw_object(view,mob);
1059 					}
1060 				else if (cl->prev!=NULL)
1061 					{
1062 					VRegion bb;
1063 					List l;
1064 					List l2;
1065 
1066 					store_redraw_object(mob);
1067 					bb = mob->bbox;
1068 					l = TEXTLINE(cl->prev)->sections;
1069 					while (l->next!=NULL)
1070 						l = l->next;
1071 
1072 					if (l==TEXTLINE(cl->prev)->sections && strlen(TEXTSEC(l)->text)==0)
1073 						{
1074 						/* nothing on previous line */
1075 						free(TEXTSEC(l));
1076 						free(l);
1077 						TEXTLINE(cl->prev)->sections = cs;
1078 						cs->prev = NULL;
1079 						if (cl->next!=NULL)
1080 							cl->next->prev = cl->prev;
1081 						cl->prev->next = cl->next;
1082 						l2 = cl;
1083 						cl = cl->prev;
1084 						free(TEXTLINE(l2));
1085 						free(l2);
1086 						}
1087 					else
1088 						{
1089 						/* something there */
1090 						l->next = cs;
1091 						cs->prev = l;
1092 						if (cl->next!=NULL)
1093 							cl->next->prev = cl->prev;
1094 						cl->prev->next = cl->next;
1095 						l2 = cl;
1096 						cl = cl->prev;
1097 						free(TEXTLINE(l2));
1098 						free(l2);
1099 						};
1100 
1101 					charpos = 0;
1102 					recalc_text_box(view,mob,FALSE,FALSE);
1103 					cx = TEXTSEC(cs)->x;
1104 					cy = TEXTLINE(cl)->y;
1105 					view->doc->o = change_bbox(view->doc->o ,mob);
1106 					send_stored_redraw_object(view,mob);
1107 					};
1108 				};
1109 			break;
1110 
1111 		case XK_Delete:
1112 			if (cs!=NULL)
1113 				{
1114 				if (charpos<19 && (int)strlen(TEXTSEC(cs)->text)>charpos)
1115 					{
1116 					VRegion bb;
1117 
1118 					store_redraw_object(mob);
1119 					strcpy(&TEXTSEC(cs)->text[charpos],&TEXTSEC(cs)->text[charpos+1]);
1120 					bb = mob->bbox;
1121 					recalc_text_box(view,mob,FALSE,FALSE);
1122 					view->doc->o = change_bbox(view->doc->o ,mob);
1123 					send_stored_redraw_object(view,mob);
1124 					}
1125 				else if (cs->next!=NULL)
1126 					{
1127 					VRegion bb;
1128 
1129 					store_redraw_object(mob);
1130 					cs = cs->next;
1131 					charpos = 0;
1132 					strcpy(TEXTSEC(cs)->text,TEXTSEC(cs)->text+1);
1133 					bb = mob->bbox;
1134 					recalc_text_box(view,mob,FALSE,FALSE);
1135 					view->doc->o = change_bbox(view->doc->o ,mob);
1136 					send_stored_redraw_object(view,mob);
1137 					}
1138 				else if (cl->next!=NULL)
1139 					{
1140 					VRegion bb;
1141 					List l;
1142 					List l2;
1143 
1144 					bb = mob->bbox;
1145 					store_redraw_object(mob);
1146 					l = TEXTLINE(cl->next)->sections;
1147 
1148 					if (strlen(TEXTSEC(l)->text)==0 && l->next==NULL)
1149 						{
1150 						/* nothing on next line */
1151 						free(TEXTSEC(l));
1152 						free(l);
1153 						l2 = cl->next;
1154 						if (cl->next->next!=NULL)
1155 							cl->next->next->prev = cl;
1156 						cl->next = cl->next->next;
1157 						free (TEXTLINE(l2));
1158 						free (l2);
1159 						}
1160 					else
1161 						{
1162 						/* something there */
1163 						if (strlen(TEXTSEC(cs)->text)!=0)
1164 							{
1165 							cs->next = l;
1166 							l->prev = cs;
1167 							l2 = cl->next;
1168 							if (cl->next->next!=NULL)
1169 								cl->next->next->prev = cl;
1170 							cl->next = cl->next->next;
1171 							free (TEXTLINE(l2));
1172 							free (l2);
1173 							}
1174 						else
1175 							{
1176 							TEXTLINE(cl)->sections = l;
1177 							free (TEXTSEC(cs));
1178 							free (cs);
1179 							cs = l;
1180 							l2 = cl->next;
1181 							if (cl->next->next!=NULL)
1182 								cl->next->next->prev = cl;
1183 							cl->next = cl->next->next;
1184 							free (TEXTLINE(l2));
1185 							free (l2);
1186 							};
1187 						};
1188 
1189 					recalc_text_box(view,mob,FALSE,FALSE);
1190 					view->doc->o = change_bbox(view->doc->o ,mob);
1191 					send_stored_redraw_object(view,mob);
1192 					};
1193 				}
1194 			break;
1195 
1196 		default:
1197 			if (!streq(str,"") && isprint(*str))
1198 				{
1199 				add_char(view, str);
1200 				recalc_text_box(view,mob,FALSE,FALSE);
1201 				view->doc->o = change_bbox(view->doc->o ,mob);
1202 				send_redraw_object(view,mob);
1203 				};
1204 			break;
1205 		};
1206 
1207 }
1208 
1209 /* draw text  */
1210 void
draw_text(Object * ob,View * view,GC gc,long x,long y,double rx,double ry,double angle)1211 draw_text(Object *ob, View *view, GC gc, long x, long y, double rx, double ry, double angle)
1212 {
1213 	Boolean clipped;
1214 	List l,l2;
1215 	VLine line; VLine *pl;
1216 	VFont *vf;
1217 	Drawable drawable;
1218 	/* init to zero to appease -Wall  */
1219 	GC tempgc=0;
1220 	uint pixw=0,pixh=0;
1221 	XImage *xi;
1222 	long dx,dy;
1223 	double sina,cosa;
1224 	long x1,y1,x2,y2;
1225 	Boolean rotcursor=FALSE;
1226 
1227 	if (ob->ob.text.node)
1228 		{
1229 		/* draw the ellipse  */
1230 		gc_lw(view,ugc, ob->ob.text.ellipse);
1231 		gc_line(ugc, ob->ob.text.ellipse);
1232 		gc_fill(fillgc, ob->ob.text.ellipse);
1233 		gc_colour(view,ugc, ob->ob.text.ellipse);
1234 		gc_fillcolour(view,fillgc, ob->ob.text.ellipse);
1235  		draw_object(ob->ob.text.ellipse,view,gc,x,y,rx,ry,0.0,0,0,0,0);
1236 		/* set our text colour back */
1237 		gc_colour(view,ugc, ob);
1238 		};
1239 
1240 	/* calc pixel co-ords of text.bbox  */
1241 	x1 = XD2P(x,view) + D2P(ob->ob.text.bbox.x1-ob->bbox.x1,view);
1242 	y1 = YD2P(y,view) + D2P(ob->ob.text.bbox.y1-ob->bbox.y1,view);
1243 	x2 = XD2P(x,view) + R(D2P((ob->ob.text.bbox.x2 - ob->ob.text.bbox.x1) +
1244 				ob->ob.text.bbox.x1-ob->bbox.x1,view),rx);
1245 	y2 = YD2P(y,view) + R(D2P((ob->ob.text.bbox.y2 - ob->ob.text.bbox.y1) +
1246 				ob->ob.text.bbox.y1-ob->bbox.y1,view),ry);
1247 
1248 	if ((ob->ob.text.angle+angle)!=0.0 && (rx!=1.0 || ry!=1.0))
1249 		{
1250 		/* this is just lazy, need to fix the positioning properly before drawing
1251 			an accurate box around rottext */
1252 
1253 		x1 = XD2P(x,view);
1254 		y1 = YD2P(y,view);
1255 		x2 = XD2P(x,view) + R(D2P(ob->bbox.x2 - ob->bbox.x1,view),rx);
1256 		y2 = YD2P(y,view) + R(D2P(ob->bbox.y2 - ob->bbox.y1,view),ry);
1257 		line.x1 = x1; line.y1 = y1; line.x2 = x2; line.y2 = y1;
1258 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1259 		if (pl!=NULL)
1260 			XDrawLine(display,view->draw_window->win, gc, pl->x1, pl->y1, pl->x2, pl->y2);
1261 		line.x1 = x2; line.y1 = y1; line.x2 = x2; line.y2 = y2;
1262 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1263 		if (pl!=NULL)
1264 			XDrawLine(display,view->draw_window->win, gc, pl->x1, pl->y1, pl->x2, pl->y2);
1265 		line.x1 = x2; line.y1 = y2; line.x2 = x1; line.y2 = y2;
1266 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1267 		if (pl!=NULL)
1268 			XDrawLine(display,view->draw_window->win, gc, pl->x1, pl->y1, pl->x2, pl->y2);
1269 		line.x1 = x1; line.y1 = y2; line.x2 = x1; line.y2 = y1;
1270 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1271 		if (pl!=NULL)
1272 			XDrawLine(display,view->draw_window->win, gc, pl->x1, pl->y1, pl->x2, pl->y2);
1273 		}
1274 	else if ((ob->ob.text.angle+angle)!=0.0 && (gc==blackxorgc || (state.editing_text && view->text_object==ob)))
1275 		{
1276 		long ccx,ccy;
1277 		GC bgc;
1278 
1279 		/* draw rotated box of text  */
1280 
1281 		if (gc!=blackxorgc)
1282 			bgc = dashgc;
1283 		else
1284 			bgc = blackxorgc;
1285 
1286 		sina = sinround(ob->ob.text.angle+angle);
1287 		cosa = cosround(ob->ob.text.angle+angle);
1288 
1289 		ccx = XD2P(x+(ob->bbox.x2-ob->bbox.x1)/2,view);
1290 		ccy = YD2P(y+(ob->bbox.y2-ob->bbox.y1)/2,view);
1291 		line.x1 = ccx + (cosa*(x1-ccx))-(sina*(y1-ccy));
1292 		line.y1 = ccy + (sina*(x1-ccx))+(cosa*(y1-ccy));
1293 		line.x2 = ccx + (cosa*(x2-ccx))-(sina*(y1-ccy));
1294 		line.y2 = ccy + (sina*(x2-ccx))+(cosa*(y1-ccy));
1295 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1296 		if (pl!=NULL)
1297 			XDrawLine(display,view->draw_window->win, bgc, pl->x1, pl->y1, pl->x2, pl->y2);
1298 		line.x1 = ccx + (cosa*(x2-ccx))-(sina*(y1-ccy));
1299 		line.y1 = ccy + (sina*(x2-ccx))+(cosa*(y1-ccy));
1300 		line.x2 = ccx + (cosa*(x2-ccx))-(sina*(y2-ccy));
1301 		line.y2 = ccy + (sina*(x2-ccx))+(cosa*(y2-ccy));
1302 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1303 		if (pl!=NULL)
1304 			XDrawLine(display,view->draw_window->win, bgc, pl->x1, pl->y1, pl->x2, pl->y2);
1305 		line.x1 = ccx + (cosa*(x2-ccx))-(sina*(y2-ccy));
1306 		line.y1 = ccy + (sina*(x2-ccx))+(cosa*(y2-ccy));
1307 		line.x2 = ccx + (cosa*(x1-ccx))-(sina*(y2-ccy));
1308 		line.y2 = ccy + (sina*(x1-ccx))+(cosa*(y2-ccy));
1309 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1310 		if (pl!=NULL)
1311 			XDrawLine(display,view->draw_window->win, bgc, pl->x1, pl->y1, pl->x2, pl->y2);
1312 		line.x1 = ccx + (cosa*(x1-ccx))-(sina*(y2-ccy));
1313 		line.y1 = ccy + (sina*(x1-ccx))+(cosa*(y2-ccy));
1314 		line.x2 = ccx + (cosa*(x1-ccx))-(sina*(y1-ccy));
1315 		line.y2 = ccy + (sina*(x1-ccx))+(cosa*(y1-ccy));
1316 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1317 		if (pl!=NULL)
1318 			XDrawLine(display,view->draw_window->win, bgc, pl->x1, pl->y1, pl->x2, pl->y2);
1319 		}
1320 	else if ((ob->ob.text.angle+angle)==0.0 && (gc==blackxorgc || (state.editing_text && view->text_object==ob)))
1321 		{
1322 		GC bgc;
1323 
1324 		/* draw straight bbox of text  */
1325 
1326 		if (gc!=blackxorgc)
1327 			bgc = dashgc;
1328 		else
1329 			bgc = blackxorgc;
1330 
1331 		line.x1 = x1;
1332 		line.y1 = y1;
1333 		line.x2 = x2;
1334 		line.y2 = y1;
1335 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1336 		if (pl!=NULL)
1337 			XDrawLine(display,view->draw_window->win, bgc, pl->x1, pl->y1, pl->x2, pl->y2);
1338 		line.x1 = x2;
1339 		line.y1 = y1;
1340 		line.x2 = x2;
1341 		line.y2 = y2;
1342 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1343 		if (pl!=NULL)
1344 			XDrawLine(display,view->draw_window->win, bgc, pl->x1, pl->y1, pl->x2, pl->y2);
1345 		line.x1 = x2;
1346 		line.y1 = y2;
1347 		line.x2 = x1;
1348 		line.y2 = y2;
1349 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1350 		if (pl!=NULL)
1351 			XDrawLine(display,view->draw_window->win, bgc, pl->x1, pl->y1, pl->x2, pl->y2);
1352 		line.x1 = x1;
1353 		line.y1 = y2;
1354 		line.x2 = x1;
1355 		line.y2 = y1;
1356 		pl = clip_line(0,0,(long)view->draw_window->w,(long)view->draw_window->h, &line, &clipped, 2);
1357 		if (pl!=NULL)
1358 			XDrawLine(display,view->draw_window->win, bgc, pl->x1, pl->y1, pl->x2, pl->y2);
1359 		};
1360 
1361 	if (gc==blackxorgc)
1362 		return;
1363 
1364 	l = ob->ob.text.lines;
1365 
1366 	if (ob->ob.text.angle!=0.0)
1367 		{
1368 		XGCValues gcv;
1369 
1370 		/* rotated. create a pixmap of text.bbox size ready for drawing onto */
1371 
1372 		pixw = D2P(ob->ob.text.bbox.x2-ob->ob.text.bbox.x1,view);
1373 		pixh = D2P(ob->ob.text.bbox.y2-ob->ob.text.bbox.y1,view);
1374 
1375 		if (pixw<1 || pixh <1)
1376 			return;
1377 
1378 		drawable = XCreatePixmap(display,view->draw_window->win, pixw, pixh, 1);
1379 		gcv.foreground = WhitePixel(display,screen);
1380 		tempgc = XCreateGC(display,drawable,GCForeground,&gcv);
1381  		XFillRectangle(display,drawable,tempgc,0,0,pixw,pixh);
1382 		gc = tempgc;
1383 		gcv.foreground = BlackPixel(display,screen);
1384  		XChangeGC(display,tempgc,GCForeground,&gcv);
1385 		dx = 0;
1386 		dy = 0;
1387 		}
1388 	else
1389 		{
1390 		/* unrotated, draw text straight to view window  */
1391 
1392 		drawable = view->draw_window->win;
1393 		gc = ugc;
1394 		dx = ob->ob.text.bbox.x1;
1395 		dy = ob->ob.text.bbox.y1;
1396 		};
1397 
1398 	while (l!=NULL)
1399 		{
1400 		l2 = TEXTLINE(l)->sections;
1401 
1402 		while (l2!=NULL)
1403 			{
1404 			/* get the font, set it, and draw the text  */
1405 			vf = get_font(TEXTSEC(l2)->num, ZPO(TEXTSEC(l2)->size,view));
1406 			if (vf!=NULL)
1407 				{
1408 				XSetFont(display, gc, vf->x->fid);
1409 				if (ob->ob.text.angle!=0.0)
1410 					XDrawString(display,drawable, gc, D2P(TEXTSEC(l2)->x,view),
1411 						D2P(TEXTLINE(l)->y,view)+vf->x->max_bounds.ascent,
1412 						TEXTSEC(l2)->text, (int)strlen(TEXTSEC(l2)->text));
1413 				else
1414 					XDrawString(display,drawable, gc, XD2P(dx+TEXTSEC(l2)->x,view),
1415 						YD2P(dy+TEXTLINE(l)->y,view)+vf->x->max_bounds.ascent,
1416 						TEXTSEC(l2)->text, (int)strlen(TEXTSEC(l2)->text));
1417 
1418 				if (l==cl && l2==cs)
1419 					{
1420 					/* cursor is here, set the values and draw if not rotated  */
1421 					line.x1 = XD2P(cx+ob->ob.text.bbox.x1,view);
1422 					line.y1 = YD2P(TEXTLINE(l)->y+ob->ob.text.bbox.y1,view);
1423 					line.x2 = XD2P(cx+ob->ob.text.bbox.x1,view);
1424 					line.y2 = YD2P(TEXTLINE(l)->y+TEXTLINE(cl)->h+ob->ob.text.bbox.y1,view);
1425 					if (ob->ob.text.angle==0.0)
1426 						{
1427 						pl = clip_line(0,0, (long)view->draw_window->w,(long)view->draw_window->h,
1428 							&line, &clipped, 2);
1429 
1430 						if (pl!=NULL)
1431 							XDrawLine(display,view->draw_window->win,
1432 								handlegc, pl->x1, pl->y1, pl->x2, pl->y2);
1433 						}
1434 					else
1435 						rotcursor = TRUE;
1436 					};
1437 				}
1438 			else
1439 				{
1440 				uint w;
1441 				uint h;
1442 
1443 				/* we can't get font from server, do very poor
1444 					estimate and greek in solid colour  */
1445 				if (l2->next==NULL)
1446 					w = D2P(ob->bbox.x2-(ob->bbox.x1+TEXTSEC(l2)->x),view);
1447 				else
1448 					w = D2P(TEXTSEC(l2->next)->x - TEXTSEC(l2)->x,view);
1449 
1450 				if (l->next==NULL)
1451 					h = D2P(ob->bbox.y2-(ob->bbox.y1+TEXTLINE(l)->y),view);
1452 				else
1453 					h = D2P(TEXTLINE(l->next)->y - TEXTLINE(l)->y,view);
1454 
1455 				XFillRectangle(display,drawable, gc,
1456 					XD2P(ob->ob.text.bbox.x1+TEXTSEC(l2)->x,view),
1457 					YD2P(ob->ob.text.bbox.y1+TEXTLINE(l)->y,view), w, h);
1458 				};
1459 
1460 			l2=l2->next;
1461 			};
1462 		l = l->next;
1463 		};
1464 
1465 	if (ob->ob.text.angle!=0.0)
1466 		{
1467 		long px,py;
1468 		long i,j;
1469 		long ccx,ccy;
1470 		/* now turn pixmap into ximage, and rotate point by point  */
1471 		/* slow but simple */
1472 		sina = sinround(ob->ob.text.angle);
1473 		cosa = cosround(ob->ob.text.angle);
1474 
1475 		xi = XGetImage(display,drawable,0,0,pixw,pixh,1,XYPixmap);
1476 		ccx = pixw/2;
1477 		ccy = pixh/2;
1478 		for (i=0;i<(long)pixw;i++)
1479 			{
1480 			for (j=0;j<(long)pixh;j++)
1481 				{
1482 				if (!XGetPixel(xi,(int)i,(int)j))
1483 					{
1484 					px = ccx+(cosa*(i-ccx))-(sina*(j-ccy));
1485 					py = ccy+(sina*(i-ccx))+(cosa*(j-ccy));
1486 					XDrawPoint(display,view->draw_window->win,ugc,
1487 						XD2P(ob->bbox.x1+(ob->ob.text.bbox.x1-ob->bbox.x1),view)+px,
1488 						YD2P(ob->bbox.y1+(ob->ob.text.bbox.y1-ob->bbox.y1),view)+py);
1489 					};
1490 				};
1491 			};
1492 		XDestroyImage(xi);
1493 		XFreePixmap(display,drawable);
1494 		XFreeGC(display,tempgc);
1495 
1496 		if (rotcursor)
1497 			{
1498 			long cenx,ceny,cx1,cy1,cx2,cy2;
1499 
1500 			/* draw the rotated cursor  */
1501 			cenx = XD2P(ob->bbox.x1+(ob->bbox.x2-ob->bbox.x1)/2,view);
1502 			ceny = YD2P(ob->bbox.y1+(ob->bbox.y2-ob->bbox.y1)/2,view);
1503 			cx1 = cenx+(cosa*(line.x1-cenx))-(sina*(line.y1-ceny));
1504 			cy1 = ceny+(sina*(line.x1-cenx))+(cosa*(line.y1-ceny));
1505 			cx2 = cenx+(cosa*(line.x2-cenx))-(sina*(line.y2-ceny));
1506 			cy2 = ceny+(sina*(line.x2-cenx))+(cosa*(line.y2-ceny));
1507 			XDrawLine(display,view->draw_window->win,handlegc,cx1,cy1,cx2,cy2);
1508 			};
1509 		};
1510 }
1511