1 #include <lib9.h>
2 #include <kernel.h>
3 #include "draw.h"
4 #include "tk.h"
5 #include "canvs.h"
6
7 #define O(t, e) ((long)(&((t*)0)->e))
8
9 /* Text Options (+ means implemented)
10 +anchor
11 +fill
12 +font
13 +justify
14 +stipple
15 +tags
16 +text
17 +width
18 */
19
20 /* Layout constants */
21 enum {
22 Cvsicursor = 1, /* Extra height of insertion cursor in canvas */
23 };
24
25 typedef struct TkCtext TkCtext;
26 struct TkCtext
27 {
28 int anchor;
29 Point anchorp;
30 int justify;
31 int icursor;
32 int focus;
33 int pixwidth;
34 int pixheight;
35 int sell;
36 int self;
37 int selfrom;
38 int sbw;
39 int width;
40 int nlines;
41 Image* stipple;
42 Image* pen;
43 char* text;
44 int tlen;
45 TkEnv *env;
46 };
47
48 static
49 TkOption textopts[] =
50 {
51 "anchor", OPTstab, O(TkCtext, anchor), tkanchor,
52 "justify", OPTstab, O(TkCtext, justify), tktabjust,
53 "width", OPTdist, O(TkCtext, width), IAUX(O(TkCtext, env)),
54 "stipple", OPTbmap, O(TkCtext, stipple), nil,
55 "text", OPTtext, O(TkCtext, text), nil,
56 nil
57 };
58
59 static
60 TkOption itemopts[] =
61 {
62 "tags", OPTctag, O(TkCitem, tags), nil,
63 "font", OPTfont, O(TkCitem, env), nil,
64 "fill", OPTcolr, O(TkCitem, env), IAUX(TkCfill),
65 nil
66 };
67
68 static char*
tkcvstextgetl(TkCtext * t,Font * font,char * start,int * len)69 tkcvstextgetl(TkCtext *t, Font *font, char *start, int *len)
70 {
71 int w, n;
72 char *lspc, *posn;
73
74 w = t->width;
75 if(w <= 0)
76 w = 1000000;
77
78 n = 0;
79 lspc = nil;
80 posn = start;
81 while(*posn && *posn != '\n') {
82 if(*posn == ' ')
83 lspc = posn;
84 n += stringnwidth(font, posn, 1);
85 if(n >= w && posn != start) {
86 if(lspc != nil)
87 posn = lspc;
88 *len = posn - start;
89 if(lspc != nil)
90 posn++;
91 return posn;
92 }
93 posn++;
94 }
95 *len = posn - start;
96 if(*posn == '\n')
97 posn++;
98 return posn;
99 }
100
101 void
tkcvstextsize(TkCitem * i)102 tkcvstextsize(TkCitem *i)
103 {
104 Point o;
105 Font *font;
106 TkCtext *t;
107 Display *d;
108 char *next, *p;
109 int len, pixw, locked;
110
111 t = TKobj(TkCtext, i);
112
113 font = i->env->font;
114 d = i->env->top->display;
115 t->pixwidth = 0;
116 t->pixheight = 0;
117
118 p = t->text;
119 if(p != nil) {
120 locked = lockdisplay(d);
121 while(*p) {
122 next = tkcvstextgetl(t, font, p, &len);
123 pixw = stringnwidth(font, p, len);
124 if(pixw > t->pixwidth)
125 t->pixwidth = pixw;
126 t->pixheight += font->height;
127 p = next;
128 }
129 if(locked)
130 unlockdisplay(d);
131 }
132
133 o = tkcvsanchor(i->p.drawpt[0], t->pixwidth, t->pixheight, t->anchor);
134
135 i->p.bb.min.x = o.x;
136 i->p.bb.min.y = o.y - Cvsicursor;
137 i->p.bb.max.x = o.x + t->pixwidth;
138 i->p.bb.max.y = o.y + t->pixheight + Cvsicursor;
139 i->p.bb = insetrect(i->p.bb, -2*t->sbw);
140 t->anchorp = subpt(o, i->p.drawpt[0]);
141 }
142
143 char*
tkcvstextcreat(Tk * tk,char * arg,char ** val)144 tkcvstextcreat(Tk* tk, char *arg, char **val)
145 {
146 char *e;
147 TkCtext *t;
148 TkCitem *i;
149 TkCanvas *c;
150 TkOptab tko[3];
151
152 c = TKobj(TkCanvas, tk);
153
154 i = tkcnewitem(tk, TkCVtext, sizeof(TkCitem)+sizeof(TkCtext));
155 if(i == nil)
156 return TkNomem;
157
158 t = TKobj(TkCtext, i);
159 t->justify = Tkleft;
160 t->anchor = Tkcenter;
161 t->sell = -1;
162 t->self = -1;
163 t->icursor = -1;
164 t->sbw = c->sborderwidth;
165 t->env = tk->env;
166
167 e = tkparsepts(tk->env->top, &i->p, &arg, 0);
168 if(e != nil) {
169 tkcvsfreeitem(i);
170 return e;
171 }
172 if(i->p.npoint != 1) {
173 tkcvsfreeitem(i);
174 return TkFewpt;
175 }
176
177 tko[0].ptr = t;
178 tko[0].optab = textopts;
179 tko[1].ptr = i;
180 tko[1].optab = itemopts;
181 tko[2].ptr = nil;
182 e = tkparse(tk->env->top, arg, tko, nil);
183 if(e != nil) {
184 tkcvsfreeitem(i);
185 return e;
186 }
187
188 e = tkcaddtag(tk, i, 1);
189 if(e != nil) {
190 tkcvsfreeitem(i);
191 return e;
192 }
193
194 t->tlen = 0;
195 if(t->text != nil)
196 t->tlen = strlen(t->text);
197
198 tkmkpen(&t->pen, i->env, t->stipple);
199 tkcvstextsize(i);
200 e = tkvalue(val, "%d", i->id);
201 if(e != nil) {
202 tkcvsfreeitem(i);
203 return e;
204 }
205
206 tkcvsappend(c, i);
207
208 tkbbmax(&c->update, &i->p.bb);
209 tkcvssetdirty(tk);
210 return nil;
211 }
212
213 char*
tkcvstextcget(TkCitem * i,char * arg,char ** val)214 tkcvstextcget(TkCitem *i, char *arg, char **val)
215 {
216 TkOptab tko[3];
217 TkCtext *t = TKobj(TkCtext, i);
218
219 tko[0].ptr = t;
220 tko[0].optab = textopts;
221 tko[1].ptr = i;
222 tko[1].optab = itemopts;
223 tko[2].ptr = nil;
224
225 return tkgencget(tko, arg, val, i->env->top);
226 }
227
228 char*
tkcvstextconf(Tk * tk,TkCitem * i,char * arg)229 tkcvstextconf(Tk *tk, TkCitem *i, char *arg)
230 {
231 char *e;
232 TkOptab tko[3];
233 TkCtext *t = TKobj(TkCtext, i);
234
235 tko[0].ptr = t;
236 tko[0].optab = textopts;
237 tko[1].ptr = i;
238 tko[1].optab = itemopts;
239 tko[2].ptr = nil;
240
241 e = tkparse(tk->env->top, arg, tko, nil);
242
243 t->tlen = 0;
244 if(t->text != nil)
245 t->tlen = strlen(t->text);
246
247 tkmkpen(&t->pen, i->env, t->stipple);
248 tkcvstextsize(i);
249
250 return e;
251 }
252
253 void
tkcvstextfree(TkCitem * i)254 tkcvstextfree(TkCitem *i)
255 {
256 TkCtext *t;
257
258 t = TKobj(TkCtext, i);
259 if(t->stipple != nil)
260 freeimage(t->stipple);
261 if(t->pen != nil)
262 freeimage(t->pen);
263 if(t->text != nil)
264 free(t->text);
265 }
266
267 void
tkcvstextdraw(Image * img,TkCitem * i,TkEnv * pe)268 tkcvstextdraw(Image *img, TkCitem *i, TkEnv *pe)
269 {
270 TkEnv *e;
271 TkCtext *t;
272 Point o, dp;
273 Rectangle r;
274 char *p, *next;
275 Image *pen;
276 int len, lw, end, start;
277
278 t = TKobj(TkCtext, i);
279
280 e = i->env;
281 pen = t->pen;
282 if(pen == nil) {
283 if (e->set & (1<<TkCfill))
284 pen = tkgc(e, TkCfill);
285 else
286 pen = img->display->black;
287 }
288
289
290 o = addpt(t->anchorp, i->p.drawpt[0]);
291 p = t->text;
292 while(p && *p) {
293 next = tkcvstextgetl(t, e->font, p, &len);
294 dp = o;
295 if(t->justify != Tkleft) {
296 lw = stringnwidth(e->font, p, len);
297 if(t->justify == Tkcenter)
298 dp.x += (t->pixwidth - lw)/2;
299 else
300 if(t->justify == Tkright)
301 dp.x += t->pixwidth - lw;
302 }
303 lw = p - t->text;
304 if(t->self != -1 && lw+len > t->self) {
305 if(t->sell >= t->self) {
306 start = t->self - lw;
307 end = t->sell - lw;
308 }
309 else {
310 start = t->sell - lw;
311 end = t->self - lw;
312 }
313 if(start < 0)
314 r.min.x = o.x;
315 else
316 r.min.x = dp.x + stringnwidth(e->font, p, start);
317 r.min.y = dp.y;
318 if(end > len)
319 r.max.x = o.x + t->pixwidth;
320 else
321 r.max.x = dp.x + stringnwidth(e->font, p, end);
322 r.max.y = dp.y + e->font->height;
323 tktextsdraw(img, r, pe, t->sbw);
324 r.max.y = dp.y;
325 if(start > 0)
326 stringn(img, dp, pen, dp, e->font, p, start);
327 if(end > start)
328 stringn(img, r.min, tkgc(pe, TkCselectfgnd), r.min, e->font, p+start, end-start);
329 if(len > end)
330 stringn(img, r.max, pen, r.max, e->font, p+end, len-end);
331 }
332 else
333 stringn(img, dp, pen, dp, e->font, p, len);
334 if(t->focus) {
335 lw = p - t->text;
336 if(t->icursor >= lw && t->icursor <= lw+len) {
337 lw = t->icursor - lw;
338 if(lw > 0)
339 lw = stringnwidth(e->font, p, lw);
340 r.min.x = dp.x + lw;
341 r.min.y = dp.y - 1;
342 r.max.x = r.min.x + 2;
343 r.max.y = r.min.y + e->font->height + 1;
344 draw(img, r, pen, nil, ZP);
345 }
346 }
347 o.y += e->font->height;
348 p = next;
349 }
350 }
351
352 char*
tkcvstextcoord(TkCitem * i,char * arg,int x,int y)353 tkcvstextcoord(TkCitem *i, char *arg, int x, int y)
354 {
355 char *e;
356 TkCpoints p;
357
358 if(arg == nil) {
359 tkxlatepts(i->p.parampt, i->p.npoint, x, y);
360 tkxlatepts(i->p.drawpt, i->p.npoint, TKF2I(x), TKF2I(y));
361 i->p.bb = rectaddpt(i->p.bb, Pt(TKF2I(x), TKF2I(y)));
362 }
363 else {
364 e = tkparsepts(i->env->top, &p, &arg, 0);
365 if(e != nil)
366 return e;
367 if(p.npoint != 1) {
368 tkfreepoint(&p);
369 return TkFewpt;
370 }
371 tkfreepoint(&i->p);
372 i->p = p;
373 tkcvstextsize(i);
374 }
375 return nil;
376 }
377
378 int
tkcvstextsrch(TkCitem * i,int x,int y)379 tkcvstextsrch(TkCitem *i, int x, int y)
380 {
381 TkCtext *t;
382 Font *font;
383 Display *d;
384 char *p, *next;
385 int n, len, locked;
386
387 t = TKobj(TkCtext, i);
388
389 n = 0;
390 font = i->env->font;
391 d = i->env->top->display;
392 p = t->text;
393 if(p == nil)
394 return 0;
395 while(*p) {
396 next = tkcvstextgetl(t, font, p, &len);
397 if(y <= font->height) {
398 locked = lockdisplay(d);
399 for(n = 0; n < len && x > stringnwidth(font, p, n+1); n++)
400 ;
401 if(locked)
402 unlockdisplay(d);
403 break;
404 }
405 y -= font->height;
406 p = next;
407 }
408 return p - t->text + n;
409 }
410
411 static char*
tkcvsparseindex(TkCitem * i,char * buf,int * index)412 tkcvsparseindex(TkCitem *i, char *buf, int *index)
413 {
414 Point o;
415 char *p;
416 int x, y;
417 TkCtext *t;
418
419 t = TKobj(TkCtext, i);
420
421 if(strcmp(buf, "end") == 0) {
422 *index = t->tlen;
423 return nil;
424 }
425 if(strcmp(buf, "sel.first") == 0) {
426 if(t->self < 0)
427 return TkBadix;
428 *index = t->self;
429 return nil;
430 }
431 if(strcmp(buf, "sel.last") == 0) {
432 if(t->sell < 0)
433 return TkBadix;
434 *index = t->sell;
435 return nil;
436 }
437 if(strcmp(buf, "insert") == 0) {
438 *index = t->icursor;
439 return nil;
440 }
441 if(buf[0] == '@') {
442 x = atoi(buf+1);
443 p = strchr(buf, ',');
444 if(p == nil)
445 return TkBadix;
446 y = atoi(p+1);
447 o = i->p.drawpt[0];
448 *index = tkcvstextsrch(i, (x-t->anchorp.x)-o.x, (y-t->anchorp.y)-o.y);
449 return nil;
450 }
451
452 if(buf[0] < '0' || buf[0] > '9')
453 return TkBadix;
454 x = atoi(buf);
455 if(x < 0)
456 x = 0;
457 if(x > t->tlen)
458 x = t->tlen;
459 *index = x;
460 return nil;
461 }
462
463 char*
tkcvstextdchar(Tk * tk,TkCitem * i,char * arg)464 tkcvstextdchar(Tk *tk, TkCitem *i, char *arg)
465 {
466 TkTop *top;
467 TkCtext *t;
468 int first, last;
469 char *e, buf[Tkmaxitem];
470
471 t = TKobj(TkCtext, i);
472
473 top = tk->env->top;
474 arg = tkword(top, arg, buf, buf+sizeof(buf), nil);
475 e = tkcvsparseindex(i, buf, &first);
476 if(e != nil)
477 return e;
478
479 last = first+1;
480 if(*arg != '\0') {
481 tkword(top, arg, buf, buf+sizeof(buf), nil);
482 e = tkcvsparseindex(i, buf, &last);
483 if(e != nil)
484 return e;
485 }
486 if(last <= first || t->tlen == 0)
487 return nil;
488
489 tkbbmax(&TKobj(TkCanvas, tk)->update, &i->p.bb);
490
491 memmove(t->text+first, t->text+last, t->tlen-last+1);
492 t->tlen -= last-first;
493
494 tkcvstextsize(i);
495 tkbbmax(&TKobj(TkCanvas, tk)->update, &i->p.bb);
496
497 tkcvssetdirty(tk);
498 return nil;
499 }
500
501 char*
tkcvstextinsert(Tk * tk,TkCitem * i,char * arg)502 tkcvstextinsert(Tk *tk, TkCitem *i, char *arg)
503 {
504 TkTop *top;
505 TkCtext *t;
506 int first, n;
507 char *e, *text, buf[Tkmaxitem];
508
509 t = TKobj(TkCtext, i);
510
511 top = tk->env->top;
512 arg = tkword(top, arg, buf, buf+sizeof(buf), nil);
513 e = tkcvsparseindex(i, buf, &first);
514 if(e != nil)
515 return e;
516
517 if(*arg == '\0')
518 return nil;
519
520 text = malloc(Tkcvstextins);
521 if(text == nil)
522 return TkNomem;
523
524 tkword(top, arg, text, text+Tkcvstextins, nil);
525 n = strlen(text);
526 t->text = realloc(t->text, t->tlen+n+1);
527 if(t->text == nil) {
528 free(text);
529 return TkNomem;
530 }
531 if(t->tlen == 0)
532 t->text[0] = '\0';
533
534 tkbbmax(&TKobj(TkCanvas, tk)->update, &i->p.bb);
535
536 memmove(t->text+first+n, t->text+first, t->tlen-first+1);
537 memmove(t->text+first, text, n);
538 t->tlen += n;
539 free(text);
540
541 tkcvstextsize(i);
542 tkbbmax(&TKobj(TkCanvas, tk)->update, &i->p.bb);
543
544 tkcvssetdirty(tk);
545 return nil;
546 }
547
548 char*
tkcvstextindex(Tk * tk,TkCitem * i,char * arg,char ** val)549 tkcvstextindex(Tk *tk, TkCitem *i, char *arg, char **val)
550 {
551 int first;
552 char *e, buf[Tkmaxitem];
553
554 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
555 e = tkcvsparseindex(i, buf, &first);
556 if(e != nil)
557 return e;
558
559 return tkvalue(val, "%d", first);
560 }
561
562 char*
tkcvstexticursor(Tk * tk,TkCitem * i,char * arg)563 tkcvstexticursor(Tk *tk, TkCitem *i, char *arg)
564 {
565 int first;
566 TkCanvas *c;
567 char *e, buf[Tkmaxitem];
568
569 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
570 e = tkcvsparseindex(i, buf, &first);
571 if(e != nil)
572 return e;
573
574 TKobj(TkCtext, i)->icursor = first;
575
576 c = TKobj(TkCanvas, tk);
577 if(c->focus == i) {
578 tkbbmax(&c->update, &i->p.bb);
579 tkcvssetdirty(tk);
580 }
581 return nil;
582 }
583
584 void
tkcvstextfocus(Tk * tk,TkCitem * i,int x)585 tkcvstextfocus(Tk *tk, TkCitem *i, int x)
586 {
587 TkCtext *t;
588 TkCanvas *c;
589
590 if(i == nil)
591 return;
592
593 t = TKobj(TkCtext, i);
594 c = TKobj(TkCanvas, tk);
595
596 if(t->focus != x) {
597 t->focus = x;
598 tkbbmax(&c->update, &i->p.bb);
599 tkcvssetdirty(tk);
600 }
601 }
602
603 void
tkcvstextclr(Tk * tk)604 tkcvstextclr(Tk *tk)
605 {
606 TkCtext *t;
607 TkCanvas *c;
608 TkCitem *item;
609
610 c = TKobj(TkCanvas, tk);
611 item = c->selection;
612 if(item == nil)
613 return;
614
615 c->selection = nil;
616 t = TKobj(TkCtext, item);
617 t->sell = -1;
618 t->self = -1;
619 tkbbmax(&c->update, &item->p.bb);
620 tkcvssetdirty(tk);
621 }
622
623 char*
tkcvstextselect(Tk * tk,TkCitem * i,char * arg,int op)624 tkcvstextselect(Tk *tk, TkCitem *i, char *arg, int op)
625 {
626 int indx;
627 TkCtext *t;
628 TkCanvas *c;
629 char *e, buf[Tkmaxitem];
630
631 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
632 e = tkcvsparseindex(i, buf, &indx);
633 if(e != nil)
634 return e;
635
636 c = TKobj(TkCanvas, tk);
637 t = TKobj(TkCtext, i);
638 switch(op) {
639 case TkCselfrom:
640 t->selfrom = indx;
641 return nil;
642 case TkCseladjust:
643 if(c->selection == i) {
644 if(abs(t->self-indx) < abs(t->sell-indx)) {
645 t->self = indx;
646 t->selfrom = t->sell;
647 }
648 else {
649 t->sell = indx;
650 t->selfrom = t->self;
651 }
652 }
653 /* No break */
654 case TkCselto:
655 if(c->selection != i)
656 tkcvstextclr(tk);
657 c->selection = i;
658 t->self = t->selfrom;
659 t->sell = indx;
660 break;
661 }
662 t->sbw = c->sborderwidth;
663 tkbbmax(&TKobj(TkCanvas, tk)->update, &i->p.bb);
664 tkcvssetdirty(tk);
665 return nil;
666 }
667