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