xref: /inferno-os/libtk/ctext.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
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