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