1 #include <lib9.h>
2 #include <kernel.h>
3 #include "draw.h"
4 #include "keyboard.h"
5 #include "tk.h"
6
7 /* Widget Commands (+ means implemented)
8 +bbox
9 +cget
10 +configure
11 +delete
12 +get
13 +icursor
14 +index
15 scan
16 +selection
17 +xview
18 +see
19 */
20
21 #define O(t, e) ((long)(&((t*)0)->e))
22
23 #define CNTL(c) ((c)&0x1f)
24 #define DEL 0x7f
25
26 /* Layout constants */
27 enum {
28 Entrypady = 0,
29 Entrypadx = 0,
30 Inswidth = 2,
31
32 Ecursoron = 1<<0,
33 Ecenter = 1<<1,
34 Eright = 1<<2,
35 Eleft = 1<<3,
36 Ewordsel = 1<<4,
37
38 Ejustify = Ecenter|Eleft|Eright
39 };
40
41 static TkStab tkjust[] =
42 {
43 "left", Eleft,
44 "right", Eright,
45 "center", Ecenter,
46 nil
47 };
48
49 static
50 TkEbind b[] =
51 {
52 {TkKey, "%W delete sel.first sel.last; %W insert insert {%A};%W see insert"},
53 {TkKey|CNTL('a'), "%W icursor 0;%W see insert;%W selection clear"},
54 {TkKey|Home, "%W icursor 0;%W see insert;%W selection clear"},
55 {TkKey|CNTL('d'), "%W delete insert; %W see insert"},
56 {TkKey|CNTL('e'), "%W icursor end; %W see insert;%W selection clear"},
57 {TkKey|End, "%W icursor end; %W see insert;%W selection clear"},
58 {TkKey|CNTL('h'), "%W tkEntryBS;%W see insert"},
59 {TkKey|CNTL('k'), "%W delete insert end;%W see insert"},
60 {TkKey|CNTL('u'), "%W delete 0 end;%W see insert"},
61 {TkKey|CNTL('w'), "%W delete sel.first sel.last; %W tkEntryBW;%W see insert"},
62 {TkKey|DEL, "%W tkEntryBS 1;%W see insert"},
63 {TkKey|CNTL('\\'), "%W selection clear"},
64 {TkKey|CNTL('/'), "%W selection range 0 end"},
65 {TkKey|Left, "%W icursor insert-1;%W selection clear;%W selection from insert;%W see insert"},
66 {TkKey|Right, "%W icursor insert+1;%W selection clear;%W selection from insert;%W see insert"},
67 {TkButton1P, "focus %W; %W tkEntryB1P %X"},
68 {TkButton1P|TkMotion, "%W tkEntryB1M %X"},
69 {TkButton1R, "%W tkEntryB1R"},
70 {TkButton1P|TkDouble, "%W tkEntryB1P %X;%W selection word @%x"},
71 {TkButton2P, "%W tkEntryB2P %x"},
72 {TkButton2P|TkMotion, "%W xview scroll %x scr"},
73 {TkFocusin, "%W tkEntryFocus in"},
74 {TkFocusout, "%W tkEntryFocus out"},
75 {TkKey|APP|'\t', ""},
76 {TkKey|BackTab, ""},
77 };
78
79 typedef struct TkEntry TkEntry;
80 struct TkEntry
81 {
82 Rune* text;
83 int textlen;
84
85 char* xscroll;
86 char* show;
87 int flag;
88 int oldx;
89
90 int icursor; /* index of insertion cursor */
91 int anchor; /* selection anchor point */
92 int sel0; /* index of start of selection */
93 int sel1; /* index of end of selection */
94
95 int x0; /* x-offset of visible area */
96
97 /* derived values */
98 int v0; /* index of first visible character */
99 int v1; /* index of last visible character + 1 */
100 int xlen; /* length of text in pixels*/
101 int xv0; /* position of first visible character */
102 int xsel0; /* position of start of selection */
103 int xsel1; /* position of end of selection */
104 int xicursor; /* position of insertion cursor */
105 };
106
107 static void blinkreset(Tk*);
108
109 static
110 TkOption opts[] =
111 {
112 "xscrollcommand", OPTtext, O(TkEntry, xscroll), nil,
113 "justify", OPTstab, O(TkEntry, flag), tkjust,
114 "show", OPTtext, O(TkEntry, show), nil,
115 nil
116 };
117
118 static int
xinset(Tk * tk)119 xinset(Tk *tk)
120 {
121 return Entrypadx + tk->highlightwidth;
122 }
123
124 static int
yinset(Tk * tk)125 yinset(Tk *tk)
126 {
127 return Entrypady + tk->highlightwidth;
128 }
129
130 static void
tksizeentry(Tk * tk)131 tksizeentry(Tk *tk)
132 {
133 if((tk->flag & Tksetwidth) == 0)
134 tk->req.width = tk->env->wzero*25 + 2*xinset(tk) + Inswidth;
135 if((tk->flag & Tksetheight) == 0)
136 tk->req.height = tk->env->font->height+ 2*yinset(tk);
137 }
138
139 int
entrytextwidth(Tk * tk,int n)140 entrytextwidth(Tk *tk, int n)
141 {
142 TkEntry *tke = TKobj(TkEntry, tk);
143 Rune c;
144 Font *f;
145
146 f = tk->env->font;
147 if (tke->show != nil) {
148 chartorune(&c, tke->show);
149 return n * runestringnwidth(f, &c, 1);
150 }
151 return runestringnwidth(f, tke->text, n);
152 }
153
154 static int
x2index(Tk * tk,int x,int * xc)155 x2index(Tk *tk, int x, int *xc)
156 {
157 TkEntry *tke = TKobj(TkEntry, tk);
158 int t0, t1, r, q;
159
160 t0 = 0;
161 t1 = tke->textlen;
162 while (t0 <= t1) {
163 r = (t0 + t1) / 2;
164 q = entrytextwidth(tk, r);
165 if (q == x) {
166 if (xc != nil)
167 *xc = q;
168 return r;
169 }
170 if (q < x)
171 t0 = r + 1;
172 else
173 t1 = r - 1;
174 }
175 if (xc != nil)
176 *xc = t1 > 0 ? entrytextwidth(tk, t1) : 0;
177 if (t1 < 0)
178 t1 = 0;
179 return t1;
180 }
181
182 /*
183 * recalculate derived values
184 */
185 static void
recalcentry(Tk * tk)186 recalcentry(Tk *tk)
187 {
188 TkEntry *tke = TKobj(TkEntry, tk);
189 int x, avail, locked;
190
191 locked = lockdisplay(tk->env->top->display);
192
193 tke->xlen = entrytextwidth(tk, tke->textlen) + Inswidth;
194
195 avail = tk->act.width - 2*xinset(tk);
196 if (tke->xlen < avail) {
197 switch(tke->flag & Ejustify) {
198 default:
199 tke->x0 = 0;
200 break;
201 case Eright:
202 tke->x0 = -(avail - tke->xlen);
203 break;
204 case Ecenter:
205 tke->x0 = -(avail - tke->xlen) / 2;
206 break;
207 }
208 }
209
210 tke->v0 = x2index(tk, tke->x0, &tke->xv0);
211 tke->v1 = x2index(tk, tk->act.width + tke->x0, &x);
212 /* perhaps include partial last character */
213 if (tke->v1 < tke->textlen && x < avail + tke->x0)
214 tke->v1++;
215 tke->xsel0 = entrytextwidth(tk, tke->sel0);
216 tke->xsel1 = entrytextwidth(tk, tke->sel1);
217 tke->xicursor = entrytextwidth(tk, tke->icursor);
218
219 if (locked)
220 unlockdisplay(tk->env->top->display);
221 }
222
223 char*
tkentry(TkTop * t,char * arg,char ** ret)224 tkentry(TkTop *t, char *arg, char **ret)
225 {
226 Tk *tk;
227 char *e;
228 TkName *names;
229 TkEntry *tke;
230 TkOptab tko[3];
231
232 tk = tknewobj(t, TKentry, sizeof(Tk)+sizeof(TkEntry));
233 if(tk == nil)
234 return TkNomem;
235
236 tk->relief = TKsunken;
237 tk->borderwidth = 1;
238 tk->flag |= Tktakefocus;
239 tk->highlightwidth = 1;
240
241 tke = TKobj(TkEntry, tk);
242
243 tko[0].ptr = tk;
244 tko[0].optab = tkgeneric;
245 tko[1].ptr = tke;
246 tko[1].optab = opts;
247 tko[2].ptr = nil;
248
249 names = nil;
250 e = tkparse(t, arg, tko, &names);
251 if(e != nil) {
252 tkfreeobj(tk);
253 return e;
254 }
255 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
256 tksizeentry(tk);
257 e = tkbindings(t, tk, b, nelem(b));
258
259 if(e != nil) {
260 tkfreeobj(tk);
261 return e;
262 }
263
264 e = tkaddchild(t, tk, &names);
265 tkfreename(names);
266 if(e != nil) {
267 tkfreeobj(tk);
268 return e;
269 }
270 tk->name->link = nil;
271 recalcentry(tk);
272
273 return tkvalue(ret, "%s", tk->name->name);
274 }
275
276 static char*
tkentrycget(Tk * tk,char * arg,char ** val)277 tkentrycget(Tk *tk, char *arg, char **val)
278 {
279 TkOptab tko[3];
280 TkEntry *tke = TKobj(TkEntry, tk);
281
282 tko[0].ptr = tk;
283 tko[0].optab = tkgeneric;
284 tko[1].ptr = tke;
285 tko[1].optab = opts;
286 tko[2].ptr = nil;
287
288 return tkgencget(tko, arg, val, tk->env->top);
289 }
290
291 void
tkfreeentry(Tk * tk)292 tkfreeentry(Tk *tk)
293 {
294 TkEntry *tke = TKobj(TkEntry, tk);
295
296 free(tke->xscroll);
297 free(tke->text);
298 free(tke->show);
299 }
300
301 static void
tkentrytext(Image * i,Rectangle s,Tk * tk,TkEnv * env)302 tkentrytext(Image *i, Rectangle s, Tk *tk, TkEnv *env)
303 {
304 TkEntry *tke = TKobj(TkEntry, tk);
305 Point dp;
306 int s0, s1, xs0, xs1, j;
307 Rectangle r;
308 Rune showr, *text;
309
310 dp = Pt(s.min.x - (tke->x0 - tke->xv0), s.min.y);
311 if (tke->show) {
312 chartorune(&showr, tke->show);
313 text = mallocz(sizeof(Rune) * (tke->textlen+1), 0);
314 if (text == nil)
315 return;
316 for (j = 0; j < tke->textlen; j++)
317 text[j] = showr;
318 } else
319 text = tke->text;
320
321 runestringn(i, dp, tkgc(env, TkCforegnd), dp, env->font,
322 text+tke->v0, tke->v1-tke->v0);
323
324 if (tke->sel0 < tke->v1 && tke->sel1 > tke->v0) {
325 if (tke->sel0 < tke->v0) {
326 s0 = tke->v0;
327 xs0 = tke->xv0 - tke->x0;
328 } else {
329 s0 = tke->sel0;
330 xs0 = tke->xsel0 - tke->x0;
331 }
332
333 if (tke->sel1 > tke->v1) {
334 s1 = tke->v1;
335 xs1 = s.max.x;
336 } else {
337 s1 = tke->sel1;
338 xs1 = tke->xsel1 - tke->x0;
339 }
340
341 r = rectaddpt(Rect(xs0, 0, xs1, env->font->height), s.min);
342 tktextsdraw(i, r, env, 1);
343 runestringn(i, r.min, tkgc(env, TkCselectfgnd), r.min, env->font,
344 text+s0, s1-s0);
345 }
346
347 if((tke->flag&Ecursoron) && tke->icursor >= tke->v0 && tke->icursor <= tke->v1) {
348 r = Rect(
349 tke->xicursor - tke->x0, 0,
350 tke->xicursor - tke->x0 + Inswidth, env->font->height
351 );
352 draw(i, rectaddpt(r, s.min), tkgc(env, TkCforegnd), nil, ZP);
353 }
354 if (tke->show)
355 free(text);
356 }
357
358 char*
tkdrawentry(Tk * tk,Point orig)359 tkdrawentry(Tk *tk, Point orig)
360 {
361 Point p;
362 TkEnv *env;
363 Rectangle r, s;
364 Image *i;
365 int xp, yp;
366
367 env = tk->env;
368
369 r.min = ZP;
370 r.max.x = tk->act.width + 2*tk->borderwidth;
371 r.max.y = tk->act.height + 2*tk->borderwidth;
372 i = tkitmp(env, r.max, TkCbackgnd);
373 if(i == nil)
374 return nil;
375
376 xp = tk->borderwidth + xinset(tk);
377 yp = tk->borderwidth + yinset(tk);
378 s = r;
379 s.min.x += xp;
380 s.max.x -= xp;
381 s.min.y += yp;
382 s.max.y -= yp;
383 tkentrytext(i, s, tk, env);
384
385 tkdrawrelief(i, tk, ZP, TkCbackgnd, tk->relief);
386
387 if (tkhaskeyfocus(tk))
388 tkbox(i, insetrect(r, tk->borderwidth), tk->highlightwidth, tkgc(tk->env, TkChighlightfgnd));
389
390 p.x = tk->act.x + orig.x;
391 p.y = tk->act.y + orig.y;
392 r = rectaddpt(r, p);
393 draw(tkimageof(tk), r, i, nil, ZP);
394
395 return nil;
396 }
397
398 char*
tkentrysh(Tk * tk)399 tkentrysh(Tk *tk)
400 {
401 TkEntry *tke = TKobj(TkEntry, tk);
402 int dx, top, bot;
403 char *val, *cmd, *v, *e;
404
405 if(tke->xscroll == nil)
406 return nil;
407
408 bot = 0;
409 top = Tkfpscalar;
410
411 if(tke->text != 0 && tke->textlen != 0) {
412 dx = tk->act.width - 2*xinset(tk);
413
414 if (tke->xlen > dx) {
415 bot = TKI2F(tke->x0) / tke->xlen;
416 top = TKI2F(tke->x0 + dx) / tke->xlen;
417 }
418 }
419
420 val = mallocz(Tkminitem, 0);
421 if(val == nil)
422 return TkNomem;
423 v = tkfprint(val, bot);
424 *v++ = ' ';
425 tkfprint(v, top);
426 cmd = mallocz(Tkminitem, 0);
427 if(cmd == nil) {
428 free(val);
429 return TkNomem;
430 }
431 sprint(cmd, "%s %s", tke->xscroll, val);
432 e = tkexec(tk->env->top, cmd, nil);
433 free(cmd);
434 free(val);
435 return e;
436 }
437
438 void
tkentrygeom(Tk * tk)439 tkentrygeom(Tk *tk)
440 {
441 char *e;
442 e = tkentrysh(tk);
443 if ((e != nil) && /* XXX - Tad: should propagate not print */
444 (tk->name != nil))
445 print("tk: xscrollcommand \"%s\": %s\n", tk->name->name, e);
446 recalcentry(tk);
447 }
448
449 static char*
tkentryconf(Tk * tk,char * arg,char ** val)450 tkentryconf(Tk *tk, char *arg, char **val)
451 {
452 char *e;
453 TkGeom g;
454 int bd;
455 TkOptab tko[3];
456 TkEntry *tke = TKobj(TkEntry, tk);
457
458 tko[0].ptr = tk;
459 tko[0].optab = tkgeneric;
460 tko[1].ptr = tke;
461 tko[1].optab = opts;
462 tko[2].ptr = nil;
463
464 if(*arg == '\0')
465 return tkconflist(tko, val);
466
467 bd = tk->borderwidth;
468 g = tk->req;
469 e = tkparse(tk->env->top, arg, tko, nil);
470 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
471 tksizeentry(tk);
472 tkgeomchg(tk, &g, bd);
473 recalcentry(tk);
474 tk->dirty = tkrect(tk, 1);
475 return e;
476 }
477
478 static char*
tkentryparseindex(Tk * tk,char * buf,int * index)479 tkentryparseindex(Tk *tk, char *buf, int *index)
480 {
481 TkEntry *tke = TKobj(TkEntry, tk);
482 TkEnv *env;
483 char *mod;
484 int i, x, locked, modstart;
485
486 modstart = 0;
487 for(mod = buf; *mod != '\0'; mod++)
488 if(*mod == '-' || *mod == '+') {
489 modstart = *mod;
490 *mod = '\0';
491 break;
492 }
493 if(strcmp(buf, "end") == 0)
494 i = tke->textlen;
495 else
496 if(strcmp(buf, "anchor") == 0)
497 i = tke->anchor;
498 else
499 if(strcmp(buf, "insert") == 0)
500 i = tke->icursor;
501 else
502 if(strcmp(buf, "sel.first") == 0)
503 i = tke->sel0;
504 else
505 if(strcmp(buf, "sel.last") == 0)
506 i = tke->sel1;
507 else
508 if(buf[0] >= '0' && buf[0] <= '9')
509 i = atoi(buf);
510 else
511 if(buf[0] == '@') {
512 x = atoi(buf+1) - xinset(tk);
513 if(tke->textlen == 0) {
514 *index = 0;
515 return nil;
516 }
517 env = tk->env;
518 locked = lockdisplay(env->top->display);
519 i = x2index(tk, x + tke->x0, nil); /* XXX could possibly select nearest character? */
520 if(locked)
521 unlockdisplay(env->top->display);
522 }
523 else
524 return TkBadix;
525
526 if(i < 0 || i > tke->textlen)
527 return TkBadix;
528 if(modstart) {
529 *mod = modstart;
530 i += atoi(mod);
531 if(i < 0)
532 i = 0;
533 if(i > tke->textlen)
534 i = tke->textlen;
535 }
536 *index = i;
537 return nil;
538 }
539
540 /*
541 * return bounding box of character at index, in coords relative to
542 * the top left position of the text.
543 */
544 static Rectangle
tkentrybbox(Tk * tk,int index)545 tkentrybbox(Tk *tk, int index)
546 {
547 TkEntry *tke;
548 TkEnv *env;
549 Display *d;
550 int x, cw, locked;
551 Rectangle r;
552
553 tke = TKobj(TkEntry, tk);
554 env = tk->env;
555
556 d = env->top->display;
557
558 locked = lockdisplay(d);
559 x = entrytextwidth(tk, index);
560 if (index < tke->textlen)
561 cw = entrytextwidth(tk, index+1) - x;
562 else
563 cw = Inswidth;
564 if(locked)
565 unlockdisplay(d);
566
567 r.min.x = x;
568 r.min.y = 0;
569 r.max.x = x + cw;
570 r.max.y = env->font->height;
571 return r;
572 }
573
574 static void
tkentrysee(Tk * tk,int index,int jump)575 tkentrysee(Tk *tk, int index, int jump)
576 {
577 TkEntry *tke = TKobj(TkEntry, tk);
578 int dx, margin;
579 Rectangle r;
580
581 r = tkentrybbox(tk, index);
582 dx = tk->act.width - 2*xinset(tk);
583 if (jump)
584 margin = dx / 4;
585 else
586 margin = 0;
587 if (r.min.x <= tke->x0 || r.max.x > tke->x0 + dx) {
588 if (r.min.x <= tke->x0) {
589 tke->x0 = r.min.x - margin;
590 if (tke->x0 < 0)
591 tke->x0 = 0;
592 } else if (r.max.x >= tke->x0 + dx) {
593 tke->x0 = r.max.x - dx + margin;
594 if (tke->x0 > tke->xlen - dx)
595 tke->x0 = tke->xlen - dx;
596 }
597 tk->dirty = tkrect(tk, 0);
598 }
599 r = rectaddpt(r, Pt(xinset(tk) - tke->x0, yinset(tk)));
600 tksee(tk, r, r.min);
601 }
602
603 static char*
tkentryseecmd(Tk * tk,char * arg,char ** val)604 tkentryseecmd(Tk *tk, char *arg, char **val)
605 {
606 int index;
607 char *e, *buf;
608
609 USED(val);
610
611 buf = mallocz(Tkmaxitem, 0);
612 if(buf == nil)
613 return TkNomem;
614 tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
615 e = tkentryparseindex(tk, buf, &index);
616 free(buf);
617 if(e != nil)
618 return e;
619
620 tkentrysee(tk, index, 1);
621 recalcentry(tk);
622
623 return nil;
624 }
625
626 static char*
tkentrybboxcmd(Tk * tk,char * arg,char ** val)627 tkentrybboxcmd(Tk *tk, char *arg, char **val)
628 {
629 TkEntry *tke = TKobj(TkEntry, tk);
630 char *r, *buf;
631 int index;
632 Rectangle bbox;
633
634 buf = mallocz(Tkmaxitem, 0);
635 if(buf == nil)
636 return TkNomem;
637 tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
638 r = tkentryparseindex(tk, buf, &index);
639 free(buf);
640 if(r != nil)
641 return r;
642 bbox = rectaddpt(tkentrybbox(tk, index), Pt(xinset(tk) - tke->x0, yinset(tk)));
643 return tkvalue(val, "%d %d %d %d", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y);
644 }
645
646 static char*
tkentryindex(Tk * tk,char * arg,char ** val)647 tkentryindex(Tk *tk, char *arg, char **val)
648 {
649 int index;
650 char *r, *buf;
651
652 buf = mallocz(Tkmaxitem, 0);
653 if(buf == nil)
654 return TkNomem;
655 tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
656 r = tkentryparseindex(tk, buf, &index);
657 free(buf);
658 if(r != nil)
659 return r;
660 return tkvalue(val, "%d", index);
661 }
662
663 static char*
tkentryicursor(Tk * tk,char * arg,char ** val)664 tkentryicursor(Tk *tk, char *arg, char **val)
665 {
666 TkEntry *tke = TKobj(TkEntry, tk);
667 int index, locked;
668 char *r, *buf;
669
670 USED(val);
671 buf = mallocz(Tkmaxitem, 0);
672 if(buf == nil)
673 return TkNomem;
674 tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
675 r = tkentryparseindex(tk, buf, &index);
676 free(buf);
677 if(r != nil)
678 return r;
679 tke->icursor = index;
680 locked = lockdisplay(tk->env->top->display);
681 tke->xicursor = entrytextwidth(tk, tke->icursor);
682 if (locked)
683 unlockdisplay(tk->env->top->display);
684
685 blinkreset(tk);
686 tk->dirty = tkrect(tk, 1);
687 return nil;
688 }
689
690 static int
adjustforins(int i,int n,int q)691 adjustforins(int i, int n, int q)
692 {
693 if (i <= q)
694 q += n;
695 return q;
696 }
697
698 static int
adjustfordel(int d0,int d1,int q)699 adjustfordel(int d0, int d1, int q)
700 {
701 if (d1 <= q)
702 q -= d1 - d0;
703 else if (d0 <= q && q <= d1)
704 q = d0;
705 return q;
706 }
707
708 static char*
tkentryget(Tk * tk,char * arg,char ** val)709 tkentryget(Tk *tk, char *arg, char **val)
710 {
711 TkTop *top;
712 TkEntry *tke;
713 int first, last;
714 char *e, *buf;
715
716 tke = TKobj(TkEntry, tk);
717 if(tke->text == nil)
718 return nil;
719
720 arg = tkskip(arg, " \t");
721 if(*arg == '\0')
722 return tkvalue(val, "%.*S", tke->textlen, tke->text);
723
724 top = tk->env->top;
725 buf = mallocz(Tkmaxitem, 0);
726 if(buf == nil)
727 return TkNomem;
728 arg = tkword(top, arg, buf, buf+Tkmaxitem, nil);
729 e = tkentryparseindex(tk, buf, &first);
730 if(e != nil) {
731 free(buf);
732 return e;
733 }
734 last = first+1;
735 tkword(top, arg, buf, buf+Tkmaxitem, nil);
736 if(buf[0] != '\0') {
737 e = tkentryparseindex(tk, buf, &last);
738 if(e != nil) {
739 free(buf);
740 return e;
741 }
742 }
743 free(buf);
744 if(last <= first || tke->textlen == 0 || first == tke->textlen)
745 return tkvalue(val, "%S", L"");
746 return tkvalue(val, "%.*S", last-first, tke->text+first);
747 }
748
749 static char*
tkentryinsert(Tk * tk,char * arg,char ** val)750 tkentryinsert(Tk *tk, char *arg, char **val)
751 {
752 TkTop *top;
753 TkEntry *tke;
754 int ins, i, n, locked;
755 char *e, *t, *text, *buf;
756 Rune *etext;
757
758 USED(val);
759 tke = TKobj(TkEntry, tk);
760
761 top = tk->env->top;
762 buf = mallocz(Tkmaxitem, 0);
763 if(buf == nil)
764 return TkNomem;
765 arg = tkword(top, arg, buf, buf+Tkmaxitem, nil);
766 e = tkentryparseindex(tk, buf, &ins);
767 free(buf);
768 if(e != nil)
769 return e;
770
771 if(*arg == '\0')
772 return nil;
773
774 n = strlen(arg) + 1;
775 if(n < Tkmaxitem)
776 n = Tkmaxitem;
777 text = malloc(n);
778 if(text == nil)
779 return TkNomem;
780
781 tkword(top, arg, text, text+n, nil);
782 n = utflen(text);
783 etext = realloc(tke->text, (tke->textlen+n+1)*sizeof(Rune));
784 if(etext == nil) {
785 free(text);
786 return TkNomem;
787 }
788 tke->text = etext;
789
790 memmove(tke->text+ins+n, tke->text+ins, (tke->textlen-ins)*sizeof(Rune));
791 t = text;
792 for(i=0; i<n; i++)
793 t += chartorune(tke->text+ins+i, t);
794 free(text);
795
796 tke->textlen += n;
797
798 tke->sel0 = adjustforins(ins, n, tke->sel0);
799 tke->sel1 = adjustforins(ins, n, tke->sel1);
800 tke->icursor = adjustforins(ins, n, tke->icursor);
801 tke->anchor = adjustforins(ins, n, tke->anchor);
802
803 locked = lockdisplay(tk->env->top->display);
804 if (ins < tke->v0)
805 tke->x0 += entrytextwidth(tk, tke->v0 + n) + (tke->x0 - tke->xv0);
806 if (locked)
807 unlockdisplay(tk->env->top->display);
808 recalcentry(tk);
809
810 e = tkentrysh(tk);
811 blinkreset(tk);
812 tk->dirty = tkrect(tk, 1);
813
814 return e;
815 }
816
817 static char*
tkentrydelete(Tk * tk,char * arg,char ** val)818 tkentrydelete(Tk *tk, char *arg, char **val)
819 {
820 TkTop *top;
821 TkEntry *tke;
822 int d0, d1, locked;
823 char *e, *buf;
824 Rune *text;
825
826 USED(val);
827
828 tke = TKobj(TkEntry, tk);
829
830 top = tk->env->top;
831 buf = mallocz(Tkmaxitem, 0);
832 if(buf == nil)
833 return TkNomem;
834 arg = tkword(top, arg, buf, buf+Tkmaxitem, nil);
835 e = tkentryparseindex(tk, buf, &d0);
836 if(e != nil) {
837 free(buf);
838 return e;
839 }
840
841 d1 = d0+1;
842 tkword(top, arg, buf, buf+Tkmaxitem, nil);
843 if(buf[0] != '\0') {
844 e = tkentryparseindex(tk, buf, &d1);
845 if(e != nil) {
846 free(buf);
847 return e;
848 }
849 }
850 free(buf);
851 if(d1 <= d0 || tke->textlen == 0 || d0 >= tke->textlen)
852 return nil;
853
854 memmove(tke->text+d0, tke->text+d1, (tke->textlen-d1)*sizeof(Rune));
855 tke->textlen -= d1 - d0;
856
857 text = realloc(tke->text, (tke->textlen+1) * sizeof(Rune));
858 if (text != nil)
859 tke->text = text;
860 tke->sel0 = adjustfordel(d0, d1, tke->sel0);
861 tke->sel1 = adjustfordel(d0, d1, tke->sel1);
862 tke->icursor = adjustfordel(d0, d1, tke->icursor);
863 tke->anchor = adjustfordel(d0, d1, tke->anchor);
864
865 locked = lockdisplay(tk->env->top->display);
866 if (d1 < tke->v0)
867 tke->x0 = entrytextwidth(tk, tke->v0 - (d1 - d0)) + (tke->x0 - tke->xv0);
868 else if (d0 < tke->v0)
869 tke->x0 = entrytextwidth(tk, d0);
870 if (locked)
871 unlockdisplay(tk->env->top->display);
872 recalcentry(tk);
873
874 e = tkentrysh(tk);
875 blinkreset(tk);
876 tk->dirty = tkrect(tk, 1);
877
878 return e;
879 }
880
881 /* Used for both backspace and DEL. If a selection exists, delete it.
882 * Otherwise delete the character to the left(right) of the insertion
883 * cursor, if any.
884 */
885 static char*
tkentrybs(Tk * tk,char * arg,char ** val)886 tkentrybs(Tk *tk, char *arg, char **val)
887 {
888 TkEntry *tke = TKobj(TkEntry, tk);
889 char *buf, *e;
890 int ix;
891
892 USED(val);
893 USED(arg);
894
895 if(tke->textlen == 0)
896 return nil;
897
898 if(tke->sel0 < tke->sel1)
899 return tkentrydelete(tk, "sel.first sel.last", nil);
900
901 buf = mallocz(Tkmaxitem, 0);
902 if(buf == nil)
903 return TkNomem;
904 tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
905 ix = -1;
906 if(buf[0] != '\0') {
907 e = tkentryparseindex(tk, buf, &ix);
908 if(e != nil) {
909 free(buf);
910 return e;
911 }
912 }
913 if(ix > -1) { /* DEL */
914 if(tke->icursor >= tke->textlen) {
915 free(buf);
916 return nil;
917 }
918 }
919 else { /* backspace */
920 if(tke->icursor == 0) {
921 free(buf);
922 return nil;
923 }
924 tke->icursor--;
925 }
926 snprint(buf, Tkmaxitem, "%d", tke->icursor);
927 e = tkentrydelete(tk, buf, nil);
928 free(buf);
929 return e;
930 }
931
932 static char*
tkentrybw(Tk * tk,char * arg,char ** val)933 tkentrybw(Tk *tk, char *arg, char **val)
934 {
935 int start;
936 Rune *text;
937 TkEntry *tke;
938 char buf[32];
939
940 USED(val);
941 USED(arg);
942
943 tke = TKobj(TkEntry, tk);
944 if(tke->textlen == 0 || tke->icursor == 0)
945 return nil;
946
947 text = tke->text;
948 start = tke->icursor-1;
949 while(start > 0 && !tkiswordchar(text[start]))
950 --start;
951 while(start > 0 && tkiswordchar(text[start-1]))
952 --start;
953
954 snprint(buf, sizeof(buf), "%d %d", start, tke->icursor);
955 return tkentrydelete(tk, buf, nil);
956 }
957
958 char*
tkentryselect(Tk * tk,char * arg,char ** val)959 tkentryselect(Tk *tk, char *arg, char **val)
960 {
961 TkTop *top;
962 int start, from, to, locked;
963 TkEntry *tke;
964 char *e, *buf;
965
966 buf = mallocz(Tkmaxitem, 0);
967 if(buf == nil)
968 return TkNomem;
969
970 tke = TKobj(TkEntry, tk);
971
972 top = tk->env->top;
973 arg = tkword(top, arg, buf, buf+Tkmaxitem, nil);
974 if(strcmp(buf, "clear") == 0) {
975 tke->sel0 = 0;
976 tke->sel1 = 0;
977 }
978 else
979 if(strcmp(buf, "from") == 0) {
980 tkword(top, arg, buf, buf+Tkmaxitem, nil);
981 e = tkentryparseindex(tk, buf, &tke->anchor);
982 tke->flag &= ~Ewordsel;
983 free(buf);
984 return e;
985 }
986 else
987 if(strcmp(buf, "to") == 0) {
988 tkword(top, arg, buf, buf+Tkmaxitem, nil);
989 e = tkentryparseindex(tk, buf, &to);
990 if(e != nil) {
991 free(buf);
992 return e;
993 }
994
995 if(to < tke->anchor) {
996 if(tke->flag & Ewordsel)
997 while(to > 0 && tkiswordchar(tke->text[to-1]))
998 --to;
999 tke->sel0 = to;
1000 tke->sel1 = tke->anchor;
1001 }
1002 else
1003 if(to >= tke->anchor) {
1004 if(tke->flag & Ewordsel)
1005 while(to < tke->textlen &&
1006 tkiswordchar(tke->text[to]))
1007 to++;
1008 tke->sel0 = tke->anchor;
1009 tke->sel1 = to;
1010 }
1011 tkentrysee(tk, to, 0);
1012 recalcentry(tk);
1013 }
1014 else
1015 if(strcmp(buf, "word") == 0) { /* inferno invention */
1016 tkword(top, arg, buf, buf+Tkmaxitem, nil);
1017 e = tkentryparseindex(tk, buf, &start);
1018 if(e != nil) {
1019 free(buf);
1020 return e;
1021 }
1022 from = start;
1023 while(from > 0 && tkiswordchar(tke->text[from-1]))
1024 --from;
1025 to = start;
1026 while(to < tke->textlen && tkiswordchar(tke->text[to]))
1027 to++;
1028 tke->sel0 = from;
1029 tke->sel1 = to;
1030 tke->anchor = from;
1031 tke->icursor = from;
1032 tke->flag |= Ewordsel;
1033 locked = lockdisplay(tk->env->top->display);
1034 tke->xicursor = entrytextwidth(tk, tke->icursor);
1035 if (locked)
1036 unlockdisplay(tk->env->top->display);
1037 }
1038 else
1039 if(strcmp(buf, "present") == 0) {
1040 e = tkvalue(val, "%d", tke->sel1 > tke->sel0);
1041 free(buf);
1042 return e;
1043 }
1044 else
1045 if(strcmp(buf, "range") == 0) {
1046 arg = tkword(top, arg, buf, buf+Tkmaxitem, nil);
1047 e = tkentryparseindex(tk, buf, &from);
1048 if(e != nil) {
1049 free(buf);
1050 return e;
1051 }
1052 tkword(top, arg, buf, buf+Tkmaxitem, nil);
1053 e = tkentryparseindex(tk, buf, &to);
1054 if(e != nil) {
1055 free(buf);
1056 return e;
1057 }
1058 tke->sel0 = from;
1059 tke->sel1 = to;
1060 if(to <= from) {
1061 tke->sel0 = 0;
1062 tke->sel1 = 0;
1063 }
1064 }
1065 else
1066 if(strcmp(buf, "adjust") == 0) {
1067 tkword(top, arg, buf, buf+Tkmaxitem, nil);
1068 e = tkentryparseindex(tk, buf, &to);
1069 if(e != nil) {
1070 free(buf);
1071 return e;
1072 }
1073 if(tke->sel0 == 0 && tke->sel1 == 0) {
1074 tke->sel0 = tke->anchor;
1075 tke->sel1 = to;
1076 }
1077 else {
1078 if(abs(tke->sel0-to) < abs(tke->sel1-to)) {
1079 tke->sel0 = to;
1080 tke->anchor = tke->sel1;
1081 }
1082 else {
1083 tke->sel1 = to;
1084 tke->anchor = tke->sel0;
1085 }
1086 }
1087 if(tke->sel0 > tke->sel1) {
1088 to = tke->sel0;
1089 tke->sel0 = tke->sel1;
1090 tke->sel1 = to;
1091 }
1092 }
1093 else {
1094 free(buf);
1095 return TkBadcm;
1096 }
1097 locked = lockdisplay(tk->env->top->display);
1098 tke->xsel0 = entrytextwidth(tk, tke->sel0);
1099 tke->xsel1 = entrytextwidth(tk, tke->sel1);
1100 if (locked)
1101 unlockdisplay(tk->env->top->display);
1102 tk->dirty = tkrect(tk, 1);
1103 free(buf);
1104 return nil;
1105 }
1106
1107
1108 static char*
tkentryb2p(Tk * tk,char * arg,char ** val)1109 tkentryb2p(Tk *tk, char *arg, char **val)
1110 {
1111 TkEntry *tke;
1112 char *buf;
1113
1114 USED(val);
1115
1116 tke = TKobj(TkEntry, tk);
1117 buf = malloc(Tkmaxitem);
1118 if (buf == nil)
1119 return TkNomem;
1120
1121 tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
1122 tke->oldx = atoi(buf);
1123 return nil;
1124 }
1125
1126 static char*
tkentryxview(Tk * tk,char * arg,char ** val)1127 tkentryxview(Tk *tk, char *arg, char **val)
1128 {
1129 int locked;
1130 TkEnv *env;
1131 TkEntry *tke;
1132 char *buf, *v;
1133 int dx, top, bot, amount, ix, x;
1134 char *e;
1135
1136 tke = TKobj(TkEntry, tk);
1137 env = tk->env;
1138 dx = tk->act.width - 2*xinset(tk);
1139
1140 buf = mallocz(Tkmaxitem, 0);
1141 if(buf == nil)
1142 return TkNomem;
1143
1144 if(*arg == '\0') {
1145 if (tke->textlen == 0 || tke->xlen < dx) {
1146 bot = TKI2F(0);
1147 top = TKI2F(1);
1148 } else {
1149 bot = TKI2F(tke->x0) / tke->xlen;
1150 top = TKI2F(tke->x0 + dx) / tke->xlen;
1151 }
1152 v = tkfprint(buf, bot);
1153 *v++ = ' ';
1154 tkfprint(v, top);
1155 e = tkvalue(val, "%s", buf);
1156 free(buf);
1157 return e;
1158 }
1159
1160 arg = tkitem(buf, arg);
1161 if(strcmp(buf, "moveto") == 0) {
1162 e = tkfracword(env->top, &arg, &top, nil);
1163 if (e != nil) {
1164 free(buf);
1165 return e;
1166 }
1167 tke->x0 = TKF2I(top*tke->xlen);
1168 }
1169 else
1170 if(strcmp(buf, "scroll") == 0) {
1171 arg = tkitem(buf, arg);
1172 amount = atoi(buf);
1173 if(*arg == 'p') /* Pages */
1174 amount *= (9*tke->xlen)/10;
1175 else
1176 if(*arg == 's') { /* Inferno-ism, "scr", must be used in the context of button2p */
1177 x = amount;
1178 amount = x < tke->oldx ? env->wzero : (x > tke->oldx ? -env->wzero : 0);
1179 tke->oldx = x;
1180 }
1181 tke->x0 += amount;
1182 }
1183 else {
1184 e = tkentryparseindex(tk, buf, &ix);
1185 if(e != nil) {
1186 free(buf);
1187 return e;
1188 }
1189 locked = lockdisplay(env->top->display);
1190 tke->x0 = entrytextwidth(tk, ix);
1191 if (locked)
1192 unlockdisplay(env->top->display);
1193 }
1194 free(buf);
1195
1196 if (tke->x0 > tke->xlen - dx)
1197 tke->x0 = tke->xlen - dx;
1198 if (tke->x0 < 0)
1199 tke->x0 = 0;
1200 recalcentry(tk);
1201 e = tkentrysh(tk);
1202 blinkreset(tk);
1203 tk->dirty = tkrect(tk, 1);
1204 return e;
1205 }
1206
1207 static void
autoselect(Tk * tk,void * v,int cancelled)1208 autoselect(Tk *tk, void *v, int cancelled)
1209 {
1210 TkEntry *tke = TKobj(TkEntry, tk);
1211 Rectangle hitr;
1212 char buf[32];
1213 Point p;
1214
1215 USED(v);
1216
1217 if (cancelled)
1218 return;
1219
1220 p = tkscrn2local(tk, Pt(tke->oldx, 0));
1221 p.y = 0;
1222 if (tkvisiblerect(tk, &hitr) && ptinrect(p, hitr))
1223 return;
1224
1225 snprint(buf, sizeof(buf), "to @%d", p.x);
1226 tkentryselect(tk, buf, nil);
1227 tkdirty(tk);
1228 tkupdate(tk->env->top);
1229 }
1230
1231 static char*
tkentryb1p(Tk * tk,char * arg,char ** ret)1232 tkentryb1p(Tk *tk, char* arg, char **ret)
1233 {
1234 TkEntry *tke = TKobj(TkEntry, tk);
1235 Point p;
1236 int i, locked, x;
1237 char buf[32], *e;
1238 USED(ret);
1239
1240 x = atoi(arg);
1241 p = tkscrn2local(tk, Pt(x, 0));
1242 sprint(buf, "@%d", p.x);
1243 e = tkentryparseindex(tk, buf, &i);
1244 if (e != nil)
1245 return e;
1246 tke->sel0 = 0;
1247 tke->sel1 = 0;
1248 tke->icursor = i;
1249 tke->anchor = i;
1250 tke->flag &= ~Ewordsel;
1251
1252 locked = lockdisplay(tk->env->top->display);
1253 tke->xsel0 = 0;
1254 tke->xsel1 = 0;
1255 tke->xicursor = entrytextwidth(tk, tke->icursor);
1256 if (locked)
1257 unlockdisplay(tk->env->top->display);
1258
1259 tke->oldx = x;
1260 blinkreset(tk);
1261 tkrepeat(tk, autoselect, nil, TkRptpause, TkRptinterval);
1262 tk->dirty = tkrect(tk, 0);
1263 return nil;
1264 }
1265
1266 static char*
tkentryb1m(Tk * tk,char * arg,char ** ret)1267 tkentryb1m(Tk *tk, char* arg, char **ret)
1268 {
1269 TkEntry *tke = TKobj(TkEntry, tk);
1270 Point p;
1271 Rectangle hitr;
1272 char buf[32];
1273 USED(ret);
1274
1275 p.x = atoi(arg);
1276 tke->oldx = p.x;
1277 p = tkscrn2local(tk, p);
1278 p.y = 0;
1279 if (!tkvisiblerect(tk, &hitr) || !ptinrect(p, hitr))
1280 return nil;
1281 snprint(buf, sizeof(buf), "to @%d", p.x);
1282 tkentryselect(tk, buf, nil);
1283 return nil;
1284 }
1285
1286 static char*
tkentryb1r(Tk * tk,char * arg,char ** ret)1287 tkentryb1r(Tk *tk, char* arg, char **ret)
1288 {
1289 USED(tk);
1290 USED(arg);
1291 USED(ret);
1292 tkcancelrepeat(tk);
1293 return nil;
1294 }
1295
1296 static void
blinkreset(Tk * tk)1297 blinkreset(Tk *tk)
1298 {
1299 TkEntry *e = TKobj(TkEntry, tk);
1300 if (!tkhaskeyfocus(tk) || tk->flag&Tkdisabled)
1301 return;
1302 e->flag |= Ecursoron;
1303 tkblinkreset(tk);
1304 }
1305
1306 static void
showcaret(Tk * tk,int on)1307 showcaret(Tk *tk, int on)
1308 {
1309 TkEntry *e = TKobj(TkEntry, tk);
1310
1311 if (on)
1312 e->flag |= Ecursoron;
1313 else
1314 e->flag &= ~Ecursoron;
1315 tk->dirty = tkrect(tk, 0);
1316 }
1317
1318 char*
tkentryfocus(Tk * tk,char * arg,char ** ret)1319 tkentryfocus(Tk *tk, char* arg, char **ret)
1320 {
1321 int on = 0;
1322 USED(ret);
1323
1324 if (tk->flag&Tkdisabled)
1325 return nil;
1326
1327 if(strcmp(arg, " in") == 0) {
1328 tkblink(tk, showcaret);
1329 on = 1;
1330 }
1331 else
1332 tkblink(nil, nil);
1333
1334 showcaret(tk, on);
1335 return nil;
1336 }
1337
1338 static
1339 TkCmdtab tkentrycmd[] =
1340 {
1341 "cget", tkentrycget,
1342 "configure", tkentryconf,
1343 "delete", tkentrydelete,
1344 "get", tkentryget,
1345 "icursor", tkentryicursor,
1346 "index", tkentryindex,
1347 "insert", tkentryinsert,
1348 "selection", tkentryselect,
1349 "xview", tkentryxview,
1350 "tkEntryBS", tkentrybs,
1351 "tkEntryBW", tkentrybw,
1352 "tkEntryB1P", tkentryb1p,
1353 "tkEntryB1M", tkentryb1m,
1354 "tkEntryB1R", tkentryb1r,
1355 "tkEntryB2P", tkentryb2p,
1356 "tkEntryFocus", tkentryfocus,
1357 "bbox", tkentrybboxcmd,
1358 "see", tkentryseecmd,
1359 nil
1360 };
1361
1362 TkMethod entrymethod = {
1363 "entry",
1364 tkentrycmd,
1365 tkfreeentry,
1366 tkdrawentry,
1367 tkentrygeom
1368 };
1369