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 /* 183 * recalculate derived values 184 */ 185 static void 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* 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* 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 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 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* 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* 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 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* 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* 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 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 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* 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* 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* 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* 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 691 adjustforins(int i, int n, int q) 692 { 693 if (i <= q) 694 q += n; 695 return q; 696 } 697 698 static int 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* 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* 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* 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* 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* 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* 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* 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* 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 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* 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* 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* 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 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 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* 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