1 #include "lib9.h" 2 #include "draw.h" 3 #include "keyboard.h" 4 #include "tk.h" 5 #include "textw.h" 6 7 /* 8 * useful text widget info to be found at: 9 * :/coordinate.systems - what coord. systems are in use 10 * textu.c:/assumed.invariants - some invariants that must be preserved 11 */ 12 13 #define istring u.string 14 #define iwin u.win 15 #define imark u.mark 16 #define iline u.line 17 18 #define FLUSH() flushimage(tk->env->top->display, 1) 19 20 #define O(t, e) ((long)(&((t*)0)->e)) 21 22 /* Layout constants */ 23 enum { 24 Textpadx = 2, 25 Textpady = 0, 26 }; 27 28 typedef struct Interval { 29 int lo; 30 int hi; 31 } Interval; 32 33 typedef struct Mprint Mprint; 34 struct Mprint 35 { 36 char* buf; 37 int ptr; 38 int len; 39 }; 40 41 typedef struct TkDump TkDump; 42 struct TkDump 43 { 44 int sgml; 45 int metrics; 46 }; 47 48 static 49 TkOption dumpopts[] = 50 { 51 "sgml", OPTbool, O(TkDump, sgml), nil, 52 "metrics", OPTbool, O(TkDump, metrics), nil, 53 nil 54 }; 55 56 static 57 TkStab tkcompare[] = 58 { 59 "<", TkLt, 60 "<=", TkLte, 61 "==", TkEq, 62 ">=", TkGte, 63 ">", TkGt, 64 "!=", TkNeq, 65 nil 66 }; 67 68 static 69 TkOption textopts[] = 70 { 71 "wrap", OPTstab, O(TkText, opts[TkTwrap]), tkwrap, 72 "spacing1", OPTnndist, O(TkText, opts[TkTspacing1]), (void *)O(Tk, env), 73 "spacing2", OPTnndist, O(TkText, opts[TkTspacing2]), (void *)O(Tk, env), 74 "spacing3", OPTnndist, O(TkText, opts[TkTspacing3]), (void *)O(Tk, env), 75 "tabs", OPTtabs, O(TkText, tabs), (void *)O(Tk, env), 76 "xscrollcommand", OPTtext, O(TkText, xscroll), nil, 77 "yscrollcommand", OPTtext, O(TkText, yscroll), nil, 78 "insertwidth", OPTnndist, O(TkText, inswidth), nil, 79 "tagshare", OPTwinp, O(TkText, tagshare), nil, 80 "propagate", OPTstab, O(TkText, propagate), tkbool, 81 "selectborderwidth", OPTnndist, O(TkText, sborderwidth), nil, 82 nil 83 }; 84 85 #define CNTL(c) ((c)&0x1f) 86 #define DEL 0x7f 87 88 static TkEbind tktbinds[] = { 89 {TkButton1P, "%W tkTextButton1 %X %Y"}, 90 {TkButton1P|TkMotion, "%W tkTextSelectTo %X %Y"}, 91 {TkButton1P|TkDouble, "%W tkTextSelectTo %X %Y double"}, 92 {TkButton1R, "%W tkTextButton1R"}, 93 {TkButton2P, "%W scan mark %x %y"}, 94 {TkButton2P|TkMotion, "%W scan dragto %x %y"}, 95 {TkKey, "%W tkTextInsert {%A}"}, 96 {TkKey|CNTL('a'), "%W tkTextSetCursor {insert linestart}"}, 97 {TkKey|Home, "%W tkTextSetCursor {insert linestart}"}, 98 {TkKey|CNTL('<'), "%W tkTextSetCursor {insert linestart}"}, 99 {TkKey|CNTL('b'), "%W tkTextSetCursor insert-1c"}, 100 {TkKey|Left, "%W tkTextSetCursor insert-1c"}, 101 {TkKey|CNTL('d'), "%W tkTextDelIns"}, 102 {TkKey|CNTL('e'), "%W tkTextSetCursor {insert lineend}"}, 103 {TkKey|End, "%W tkTextSetCursor {insert lineend}"}, 104 {TkKey|CNTL('>'), "%W tkTextSetCursor {insert lineend}"}, 105 {TkKey|CNTL('f'), "%W tkTextSetCursor insert+1c"}, 106 {TkKey|Right, "%W tkTextSetCursor insert+1c"}, 107 {TkKey|CNTL('h'), "%W tkTextDelIns -c"}, 108 {TkKey|DEL, "%W tkTextDelIns"}, 109 {TkKey|CNTL('k'), "%W tkTextDelIns +l"}, 110 {TkKey|CNTL('n'), "%W tkTextSetCursor {insert+1l}"}, 111 {TkKey|Down, "%W tkTextSetCursor {insert+1l}"}, 112 {TkKey|CNTL('o'), "%W tkTextInsert {\n}; %W mark set insert insert-1c"}, 113 {TkKey|CNTL('p'), "%W tkTextSetCursor {insert-1l}"}, 114 {TkKey|Up, "%W tkTextSetCursor {insert-1l}"}, 115 {TkKey|CNTL('u'), "%W tkTextDelIns -l"}, 116 {TkKey|CNTL('v'), "%W yview scroll 0.75 page"}, 117 {TkKey|Pgdown, "%W yview scroll 0.75 page"}, 118 {TkKey|CNTL('w'), "%W tkTextDelIns -w"}, 119 {TkKey|Pgup, "%W yview scroll -0.75 page"}, 120 {TkButton4P, "%W yview scroll -0.2 page"}, 121 {TkButton5P, "%W yview scroll 0.2 page"}, 122 {TkFocusout, "%W tkTextCursor delete"}, 123 {TkKey|APP|'\t', ""}, 124 {TkKey|BackTab, ""}, 125 }; 126 127 static int tktclickmatch(TkText *, int, int, int, TkTindex *); 128 static void tktdoubleclick(TkText *, TkTindex *, TkTindex *); 129 static char* tktdrawline(Image*, Tk*, TkTline*, Point); 130 static void tktextcursordraw(Tk *, int); 131 static char* tktsetscroll(Tk*, int); 132 static void tktsetclip(Tk *); 133 static char* tktview(Tk*, char*, char**, int, int*, int, int); 134 static Interval tkttranslate(Tk*, Interval, int); 135 static void tktfixscroll(Tk*, Point); 136 static void tktnotdrawn(Tk*, int, int, int); 137 static void tktdrawbg(Tk*, int, int, int); 138 static int tktwidbetween(Tk*, int, TkTindex*, TkTindex*); 139 static int tktpostspace(Tk*, TkTline*); 140 static int tktprespace(Tk*, TkTline*); 141 static void tktsee(Tk*, TkTindex*, int); 142 static Point tktrelpos(Tk*); 143 static void autoselect(Tk*, void*, int); 144 static void blinkreset(Tk*); 145 146 /* debugging */ 147 extern int tktdbg; 148 extern void tktprinttext(TkText*); 149 extern void tktprintindex(TkTindex*); 150 extern void tktprintitem(TkTitem*); 151 extern void tktprintline(TkTline*); 152 extern void tktcheck(TkText*, char*); 153 extern int tktutfpos(char *, int); 154 155 char* 156 tktext(TkTop *t, char* arg, char **ret) 157 { 158 Tk *tk; 159 char *e; 160 TkEnv *ev; 161 TkTline *l; 162 TkTitem *it = nil; 163 TkName *names = nil; 164 TkTtaginfo *ti = nil; 165 TkOptab tko[3]; 166 TkTmarkinfo *mi = nil; 167 TkText *tkt, *tktshare; 168 169 tk = tknewobj(t, TKtext, sizeof(Tk)+sizeof(TkText)); 170 if(tk == nil) 171 return TkNomem; 172 173 tkt = TKobj(TkText, tk); 174 175 tk->relief = TKsunken; 176 tk->borderwidth = 1; 177 tk->ipad.x = Textpadx * 2; 178 tk->ipad.y = Textpady * 2; 179 tk->flag |= Tktakefocus; 180 tkt->sborderwidth = 0; 181 tkt->inswidth = 2; 182 tkt->cur_flag = 0; /* text cursor doesn't show up initially */ 183 tkt->opts[TkTwrap] = Tkwrapchar; 184 tkt->opts[TkTrelief] = TKflat; 185 tkt->opts[TkTjustify] = Tkleft; 186 tkt->propagate = BoolX; 187 188 tko[0].ptr = tk; 189 tko[0].optab = tkgeneric; 190 tko[1].ptr = tkt; 191 tko[1].optab = textopts; 192 tko[2].ptr = nil; 193 194 tk->req.width = tk->env->wzero*Textwidth; 195 tk->req.height = tk->env->font->height*Textheight; 196 197 names = nil; 198 e = tkparse(t, arg, tko, &names); 199 if(e != nil) 200 goto err; 201 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 202 if(names == nil) { 203 /* tkerr(t, arg); XXX */ 204 e = TkBadwp; 205 goto err; 206 } 207 208 if(tkt->tagshare != nil) { 209 tkputenv(tk->env); 210 tk->env = tkt->tagshare->env; 211 tk->env->ref++; 212 } 213 214 if(tk->flag&Tkdisabled) 215 tkt->inswidth = 0; 216 217 if(tkt->tabs == nil) { 218 tkt->tabs = malloc(sizeof(TkTtabstop)); 219 if(tkt->tabs == nil) 220 goto err; 221 tkt->tabs->pos = 8*tk->env->wzero; 222 tkt->tabs->justify = Tkleft; 223 tkt->tabs->next = nil; 224 } 225 226 if(tkt->tagshare != nil) { 227 tktshare = TKobj(TkText, tkt->tagshare); 228 tkt->tags = tktshare->tags; 229 tkt->nexttag = tktshare->nexttag; 230 } 231 else { 232 /* Note: sel should have id == TkTselid == 0 */ 233 e = tktaddtaginfo(tk, "sel", &ti); 234 if(e != nil) 235 goto err; 236 237 tkputenv(ti->env); 238 ti->env = tknewenv(t); 239 if(ti->env == nil) 240 goto err; 241 242 ev = ti->env; 243 ev->colors[TkCbackgnd] = tk->env->colors[TkCselectbgnd]; 244 ev->colors[TkCbackgndlght] = tk->env->colors[TkCselectbgndlght]; 245 ev->colors[TkCbackgnddark] = tk->env->colors[TkCselectbgnddark]; 246 ev->colors[TkCforegnd] = tk->env->colors[TkCselectfgnd]; 247 ev->set = (1<<TkCbackgnd)|(1<<TkCbackgndlght)| 248 (1<<TkCbackgnddark)|(1<<TkCforegnd); 249 250 ti->opts[TkTborderwidth] = tkt->sborderwidth; 251 if(tkt->sborderwidth > 0) 252 ti->opts[TkTrelief] = TKraised; 253 } 254 255 e = tktaddmarkinfo(tkt, "current", &mi); 256 if(e != nil) 257 goto err; 258 259 e = tktaddmarkinfo(tkt, "insert", &mi); 260 if(e != nil) 261 goto err; 262 263 tkt->start.flags = TkTfirst|TkTlast; 264 tkt->end.flags = TkTlast; 265 266 e = tktnewitem(TkTnewline, 0, &it); 267 268 if(e != nil) 269 goto err; 270 271 e = tktnewline(TkTfirst|TkTlast, it, &tkt->start, &tkt->end, &l); 272 if(e != nil) 273 goto err; 274 275 e = tktnewitem(TkTmark, 0, &it); 276 if(e != nil) 277 goto err; 278 279 it->next = l->items; 280 l->items = it; 281 it->imark = mi; 282 mi->cur = it; 283 tkt->nlines = 1; 284 tkt->scrolltop[Tkvertical] = -1; 285 tkt->scrolltop[Tkhorizontal] = -1; 286 tkt->scrollbot[Tkvertical] = -1; 287 tkt->scrollbot[Tkhorizontal] = -1; 288 289 if(tkt->tagshare != nil) 290 tk->binds = tkt->tagshare->binds; 291 else { 292 e = tkbindings(t, tk, tktbinds, nelem(tktbinds)); 293 294 if(e != nil) 295 goto err; 296 } 297 if (tkt->propagate == BoolT) { 298 if ((tk->flag & Tksetwidth) == 0) 299 tk->req.width = tktmaxwid(tkt->start.next); 300 if ((tk->flag & Tksetheight) == 0) 301 tk->req.height = tkt->end.orig.y; 302 } 303 304 e = tkaddchild(t, tk, &names); 305 tkfreename(names); 306 if(e != nil) 307 goto err; 308 tk->name->link = nil; 309 310 return tkvalue(ret, "%s", tk->name->name); 311 312 err: 313 /* XXX it's possible there's a memory leak here */ 314 tkfreeobj(tk); 315 return e; 316 } 317 318 /* 319 * There are four coordinate systems of interest: 320 * S - screen coordinate system (i.e. top left corner of 321 * inferno screen is (0,0) in S space.) 322 * I - image coordinate system (i.e. top left corner of 323 * tkimageof(this widget) is (0,0) in I space.) 324 * T - text coordinate system (i.e., top left of first line 325 * is at (0,0) in T space.) 326 * V - view coordinate system (i.e., top left of visible 327 * portion of widget is at (0,0) in V space.) 328 * 329 * A point P in the four systems (Ps, Pi, Pt, Pv) satisfies: 330 * Pt = Ps - deltast 331 * Pv = Ps - deltasv 332 * Pv = Pi - deltaiv 333 * (where deltast is vector from S origin to T origin; 334 * deltasv is vector from S origin to V origin; 335 * deltaiv is vector from I origin to V origin) 336 * 337 * We keep deltatv, deltasv, and deltaiv in tkt. 338 * Deltatv is updated by scrolling. 339 * Deltasv is updated by geom changes: 340 * tkposn(tk)+ipad/2 341 * Deltaiv is affected by geom changes and the call to the draw function: 342 * tk->act+orig+ipad/2+(bw,bw) (orig is the parameter to tkdrawtext), 343 * 344 * We can derive 345 * Ps = Pt + deltast 346 * = Pt + deltasv - deltatv 347 * 348 * Pv = Pt - deltatv 349 * 350 * Here are various coordinates in the text widget according 351 * to which coordinate system they use: 352 * 353 * S - Mouse coordinates (coming in to tktextevent); 354 * the deltasv parameter to tkdrawtext; 355 * coords in tkt->image, where drawing is done to 356 * (to get same bit-alignment as screen, for fast transfer) 357 * T - orig in TkTlines 358 * V - %x,%y delivered via binds to TkText or its tags 359 360 * Note deltasv changes underneath us, so is calculated on the fly 361 * when it needs to be (in tktextevent). 362 * 363 */ 364 static void 365 tktsetdeltas(Tk *tk, Point orig) 366 { 367 TkText *tkt = TKobj(TkText, tk); 368 369 tkt->deltaiv.x = orig.x + tk->act.x + tk->ipad.x/2 + tk->borderwidth; 370 tkt->deltaiv.y = orig.y + tk->act.y + tk->ipad.y/2 + tk->borderwidth; 371 } 372 373 static Point 374 tktrelpos(Tk *sub) 375 { 376 Tk *tk; 377 TkTindex ix; 378 Rectangle r; 379 Point ans; 380 381 tk = sub->parent; 382 if(tk == nil) 383 return ZP; 384 385 if(tktfindsubitem(sub, &ix)) { 386 r = tktbbox(tk, &ix); 387 ans.x = r.min.x; 388 ans.y = r.min.y; 389 return r.min; 390 } 391 return ZP; 392 } 393 394 static void 395 tktreplclipr(Image *dst, Rectangle r) 396 { 397 int locked; 398 399 locked = lockdisplay(dst->display); 400 replclipr(dst, 0, r); 401 if(locked) 402 unlockdisplay(dst->display); 403 } 404 405 char* 406 tkdrawtext(Tk *tk, Point orig) 407 { 408 int vh; 409 Image *dst; 410 TkText *tkt; 411 TkTline *l, *lend; 412 Point p, deltait; 413 Rectangle oclipr; 414 int reldone = 1; 415 char *e; 416 tkt = TKobj(TkText, tk); 417 dst = tkimageof(tk); 418 if (dst == nil) 419 return nil; 420 tkt->image = dst; 421 tktsetdeltas(tk, orig); 422 tkt->tflag |= TkTdrawn|TkTdlocked; 423 oclipr = dst->clipr; 424 tktsetclip(tk); 425 426 if(tk->flag&Tkrefresh) { 427 reldone = 0; 428 tktnotdrawn(tk, 0, tkt->end.orig.y, 1); 429 } 430 tk->flag &= ~Tkrefresh; 431 432 deltait = subpt(tkt->deltaiv, tkt->deltatv); 433 vh = tk->act.height - tk->ipad.y/2; 434 lend = &tkt->end; 435 for(l = tkt->start.next; l != lend; l = l->next) { 436 if(l->orig.y+l->height < tkt->deltatv.y) 437 continue; 438 if(l->orig.y > tkt->deltatv.y + vh) 439 break; 440 if(!(l->flags&TkTdrawn)) { 441 e = tktdrawline(dst, tk, l, deltait); 442 if(e != nil) 443 return e; 444 } 445 } 446 447 tktreplclipr(dst, oclipr); 448 if(!reldone) { 449 p.x = orig.x + tk->act.x; 450 p.y = orig.y + tk->act.y; 451 tkdrawrelief(dst, tk, p, TkCbackgnd, tk->relief); 452 } 453 tkt->tflag &= ~TkTdlocked; 454 455 return nil; 456 } 457 458 /* 459 * Set the clipping rectangle of the destination image to the 460 * intersection of the current clipping rectangle and the area inside 461 * the text widget that needs to be redrawn. 462 * The caller should save the old one and restore it later. 463 */ 464 static void 465 tktsetclip(Tk *tk) 466 { 467 Rectangle r; 468 Image *dst; 469 TkText *tkt = TKobj(TkText, tk); 470 471 dst = tkt->image; 472 r.min = tkt->deltaiv; 473 r.max.x = r.min.x + tk->act.width - tk->ipad.x / 2; 474 r.max.y = r.min.y + tk->act.height - tk->ipad.y / 2; 475 476 if(!rectclip(&r, dst->clipr)) 477 r.max = r.min; 478 tktreplclipr(dst, r); 479 } 480 481 static char* 482 tktdrawline(Image *i, Tk *tk, TkTline *l, Point deltait) 483 { 484 Tk *sub; 485 Font *f; 486 Image *bg; 487 Point p, q; 488 Rectangle r; 489 TkText *tkt; 490 TkTitem *it, *z; 491 int bevtop, bevbot; 492 TkEnv *e, *et, *env; 493 int *opts; 494 int o, bd, ul, ov, h, w, la, lh, cursorx, join; 495 char *err; 496 497 env = mallocz(sizeof(TkEnv), 0); 498 if(env == nil) 499 return TkNomem; 500 opts = mallocz(TkTnumopts*sizeof(int), 0); 501 if(opts == nil) { 502 free(env); 503 return TkNomem; 504 } 505 tkt = TKobj(TkText, tk); 506 e = tk->env; 507 et = env; 508 et->top = e->top; 509 f = e->font; 510 511 /* l->orig is in T space, p is in I space */ 512 la = l->ascent; 513 lh = l->height; 514 p = addpt(l->orig, deltait); 515 p.y += la; 516 /* if(tktdbg){print("drawline, p=(%d,%d), f->a=%d, f->h=%d\n", p.x, p.y, f->ascent, f->height); tktprintline(l);} */ 517 cursorx = -1000; 518 join = 0; 519 for(it = l->items; it != nil; it = it->next) { 520 bg = tkgc(e, TkCbackgnd); 521 if(tktanytags(it)) { 522 tkttagopts(tk, it, opts, env, nil, 1); 523 if(e->colors[TkCbackgnd] != et->colors[TkCbackgnd]) { 524 bg = tkgc(et, TkCbackgnd); 525 r.min = p; 526 r.min.y -= la; 527 r.max.x = r.min.x + it->width; 528 r.max.y = r.min.y + lh; 529 draw(i, r, bg, nil, ZP); 530 } 531 o = opts[TkTrelief]; 532 bd = opts[TkTborderwidth]; 533 if((o == TKsunken || o == TKraised) && bd > 0) { 534 /* fit relief inside item bounding box */ 535 536 q.x = p.x; 537 q.y = p.y - la; 538 if(it->width < 2*bd) 539 bd = it->width / 2; 540 if(lh < 2*bd) 541 bd = lh / 2; 542 w = it->width - 2*bd; 543 h = lh - 2*bd; 544 if(o == TKraised) { 545 bevtop = TkLightshade; 546 bevbot = TkDarkshade; 547 } 548 else { 549 bevtop = TkDarkshade; 550 bevbot = TkLightshade; 551 } 552 553 tkbevel(i, q, w, h, bd, 554 tkgc(et, TkCbackgnd+bevtop), tkgc(et, TkCbackgnd+bevbot)); 555 556 /* join relief between adjacent items if tags match */ 557 if(join) { 558 r.min.x = q.x; 559 r.max.x = q.x + bd; 560 r.min.y = q.y + bd; 561 r.max.y = r.min.y + h; 562 draw(i, r, bg, nil, ZP); 563 r.min.y = r.max.y; 564 r.max.y = r.min.y + bd; 565 draw(i, r, tkgc(et, TkCbackgnd+bevbot), nil, ZP); 566 } 567 for(z = it->next; z != nil && z->kind == TkTmark; ) 568 z = z->next; 569 if(z != nil && tktsametags(z, it)) { 570 r.min.x = q.x + bd + w; 571 r.max.x = r.min.x + bd; 572 r.min.y = q.y; 573 r.max.y = q.y + bd; 574 draw(i, r, tkgc(et, TkCbackgnd+bevtop), nil, ZP); 575 r.min.y = r.max.y; 576 r.max.y = r.min.y + h; 577 draw(i, r, bg, nil, ZP); 578 join = 1; 579 } 580 else 581 join = 0; 582 } 583 o = opts[TkToffset]; 584 ul = opts[TkTunderline]; 585 ov = opts[TkToverstrike]; 586 } 587 else { 588 et->font = f; 589 et->colors[TkCforegnd] = e->colors[TkCforegnd]; 590 o = 0; 591 ul = 0; 592 ov = 0; 593 } 594 595 switch(it->kind) { 596 case TkTascii: 597 case TkTrune: 598 q.x = p.x; 599 q.y = p.y - env->font->ascent - o; 600 /*if(tktdbg)print("q=(%d,%d)\n", q.x, q.y);*/ 601 string(i, q, tkgc(et, TkCforegnd), q, env->font, it->istring); 602 if(ov == BoolT) { 603 r.min.x = q.x; 604 r.max.x = r.min.x + it->width; 605 r.min.y = q.y + 2*env->font->ascent/3; 606 r.max.y = r.min.y + 2; 607 draw(i, r, tkgc(et, TkCforegnd), nil, ZP); 608 } 609 if(ul == BoolT) { 610 r.min.x = q.x; 611 r.max.x = r.min.x + it->width; 612 r.max.y = p.y - la + lh; 613 r.min.y = r.max.y - 2; 614 draw(i, r, tkgc(et, TkCforegnd), nil, ZP); 615 } 616 break; 617 case TkTmark: 618 if((it->imark != nil) 619 && strcmp(it->imark->name, "insert") == 0) { 620 cursorx = p.x - 1; 621 } 622 break; 623 case TkTwin: 624 sub = it->iwin->sub; 625 if(sub != nil) { 626 int dirty; 627 sub->flag |= Tkrefresh; 628 sub->dirty = tkrect(sub, 1); 629 err = tkdrawslaves(sub, p, &dirty); 630 if(err != nil) { 631 free(opts); 632 free(env); 633 return err; 634 } 635 } 636 break; 637 } 638 p.x += it->width; 639 } 640 l->flags |= TkTdrawn; 641 642 /* do cursor last, so not overwritten by later items */ 643 if(cursorx != -1000 && tkt->inswidth > 0) { 644 r.min.x = cursorx; 645 r.min.y = p.y - la; 646 r.max.x = r.min.x + tkt->inswidth; 647 r.max.y = r.min.y + lh; 648 r = rectsubpt(r, deltait); 649 if (!eqrect(tkt->cur_rec, r)) 650 blinkreset(tk); 651 tkt->cur_rec = r; 652 if(tkt->cur_flag) 653 tktextcursordraw(tk, TkCforegnd); 654 } 655 656 free(opts); 657 free(env); 658 return nil; 659 } 660 661 static void 662 tktextcursordraw(Tk *tk, int color) 663 { 664 Rectangle r; 665 TkText *tkt; 666 Image *i; 667 668 tkt = TKobj(TkText, tk); 669 670 r = rectaddpt(tkt->cur_rec, subpt(tkt->deltaiv, tkt->deltatv)); 671 672 /* check the cursor with widget boundary */ 673 /* do nothing if entire cursor outside widget boundary */ 674 if( ! ( r.max.x < tkt->deltaiv.x || 675 r.min.x > tkt->deltaiv.x + tk->act.width || 676 r.max.y < tkt->deltaiv.y || 677 r.min.y > tkt->deltaiv.y + tk->act.height)) { 678 679 /* clip rectangle if extends beyond widget boundary */ 680 if (r.min.x < tkt->deltaiv.x) 681 r.min.x = tkt->deltaiv.x; 682 if (r.max.x > tkt->deltaiv.x + tk->act.width) 683 r.max.x = tkt->deltaiv.x + tk->act.width; 684 if (r.min.y < tkt->deltaiv.y) 685 r.min.y = tkt->deltaiv.y; 686 if (r.max.y > tkt->deltaiv.y + tk->act.height) 687 r.max.y = tkt->deltaiv.y + tk->act.height; 688 i = tkimageof(tk); 689 if (i != nil) 690 draw(i, r, tkgc(tk->env, color), nil, ZP); 691 } 692 } 693 694 static void 695 blinkreset(Tk *tk) 696 { 697 TkText *tkt = TKobj(TkText, tk); 698 if (!tkhaskeyfocus(tk) || tk->flag&Tkdisabled) 699 return; 700 tkt->cur_flag = 1; 701 tkblinkreset(tk); 702 } 703 704 static void 705 showcaret(Tk *tk, int on) 706 { 707 TkText *tkt = TKobj(TkText, tk); 708 TkTline *l, *lend; 709 TkTitem *it; 710 711 tkt->cur_flag = on; 712 lend = &tkt->end; 713 for(l = tkt->start.next; l != lend; l = l->next) { 714 for (it = l->items; it != nil; it = it->next) { 715 if (it->kind == TkTmark && it->imark != nil && 716 strcmp(it->imark->name, "insert") == 0) { 717 if (on) { 718 tktextcursordraw(tk, TkCforegnd); 719 tk->dirty = tkrect(tk, 1); 720 } else 721 tktnotdrawn(tk, l->orig.y, l->orig.y+l->height, 0); 722 tkdirty(tk); 723 return; 724 } 725 } 726 } 727 } 728 729 char* 730 tktextcursor(Tk *tk, char* arg, char **ret) 731 { 732 int on = 0; 733 USED(ret); 734 735 if (tk->flag&Tkdisabled) 736 return nil; 737 738 if(strcmp(arg, " insert") == 0) { 739 tkblink(tk, showcaret); 740 on = 1; 741 } 742 else 743 tkblink(nil, nil); 744 745 showcaret(tk, on); 746 return nil; 747 } 748 749 /* 750 * Insert string s just before ins, but don't worry about geometry values. 751 * Don't worry about doing wrapping correctly, but break long strings 752 * into pieces to avoid bad behavior in the wrapping code of tktfixgeom. 753 * If tagit != 0, use its tags, else use the intersection of tags of 754 * non cont or mark elements just before and just after insertion point. 755 * (At beginning and end of widget, just use the tags of one adjacent item). 756 * Keep *ins up-to-date. 757 */ 758 char* 759 tktinsert(Tk *tk, TkTindex *ins, char *s, TkTitem *tagit) 760 { 761 int c, n, nextra, nmax, atend, atbeg; 762 char *e, *p; 763 Rune r; 764 TkTindex iprev, inext; 765 TkTitem *i, *utagit; 766 TkText *tkt = TKobj(TkText, tk); 767 768 e = tktsplititem(ins); 769 if(e != nil) 770 return e; 771 772 /* if no tags give, use intersection of previous and next char tags */ 773 774 nextra = 0; 775 n = tk->env->wzero; 776 if(n <= 0) 777 n = 8; 778 nmax = tk->act.width - tk->ipad.x; 779 if(nmax <= 0) { 780 if (tkt->propagate != BoolT || (tk->flag & Tksetwidth)) 781 nmax = tk->req.width; 782 if(nmax <= 0) 783 nmax = 60*n; 784 } 785 nmax = (nmax + n - 1) / n; 786 utagit = nil; 787 if(tagit == nil) { 788 inext = *ins; 789 tktadjustind(tkt, TkTbycharstart, &inext); 790 atend = (inext.item->next == nil && inext.line->next == &tkt->end); 791 if(atend || tktanytags(inext.item)) { 792 iprev = *ins; 793 tktadjustind(tkt, TkTbycharback, &iprev); 794 atbeg = (iprev.line->prev == &tkt->start && iprev.line->items == iprev.item); 795 if(atbeg || tktanytags(iprev.item)) { 796 nextra = 0; 797 if(!atend) 798 nextra = inext.item->tagextra; 799 if(!atbeg && iprev.item->tagextra > nextra) 800 nextra = iprev.item->tagextra; 801 e = tktnewitem(TkTascii, nextra, &utagit); 802 if(e != nil) 803 return e; 804 if(!atend) { 805 tkttagcomb(utagit, inext.item, 1); 806 if(!atbeg) 807 tkttagcomb(utagit, iprev.item, 0); 808 } 809 else if(!atbeg) 810 tkttagcomb(utagit, iprev.item, 1); 811 tagit = utagit; 812 } 813 } 814 } 815 else 816 nextra = tagit->tagextra; 817 818 while((c = *s) != '\0') { 819 e = tktnewitem(TkTascii, nextra, &i); 820 if(e != nil) { 821 if(utagit != nil) 822 free(utagit); 823 return e; 824 } 825 826 if(tagit != nil) 827 tkttagcomb(i, tagit, 1); 828 829 if(c == '\n') { 830 i->kind = TkTnewline; 831 tkt->nlines++; 832 s++; 833 } 834 else 835 if(c == '\t') { 836 i->kind = TkTtab; 837 s++; 838 } 839 else { 840 p = s; 841 n = 0; 842 i->kind = TkTascii; 843 while(c != '\0' && c != '\n' && c != '\t' && n < nmax){ 844 s += chartorune(&r, s); 845 c = *s; 846 n++; 847 } 848 /* 849 * if more bytes than runes, then it's not all ascii, so create a TkTrune item 850 */ 851 if(s - p > n) 852 i->kind = TkTrune; 853 n = s - p; 854 i->istring = malloc(n+1); 855 if(i->istring == nil) { 856 tktfreeitems(tkt, i, 1); 857 if(utagit != nil) 858 free(utagit); 859 return TkNomem; 860 } 861 memmove(i->istring, p, n); 862 i->istring[n] = '\0'; 863 } 864 e = tktiteminsert(tkt, ins, i); 865 if(e != nil) { 866 if(utagit != nil) 867 free(utagit); 868 tktfreeitems(tkt, i, 1); 869 return e; 870 } 871 } 872 873 if(utagit != nil) 874 free(utagit); 875 return nil; 876 } 877 878 void 879 tktextsize(Tk *tk, int dogeom) 880 { 881 TkText *tkt; 882 TkGeom g; 883 tkt = TKobj(TkText, tk); 884 if (tkt->propagate == BoolT) { 885 g = tk->req; 886 if ((tk->flag & Tksetwidth) == 0) 887 tk->req.width = tktmaxwid(tkt->start.next); 888 if ((tk->flag & Tksetheight) == 0) 889 tk->req.height = tkt->end.orig.y; 890 if (dogeom) 891 tkgeomchg(tk, &g, tk->borderwidth); 892 } 893 } 894 895 static int 896 maximum(int a, int b) 897 { 898 if (a > b) 899 return a; 900 return b; 901 } 902 903 /* 904 * For lines l1->next, ..., l2, fix up the geometry 905 * elements of constituent TkTlines and TkTitems. 906 * This involves doing proper line wrapping, and calculating item 907 * widths and positions. 908 * Also, merge any adjacent TkTascii/TkTrune items with the same tags. 909 * Finally, bump the y component of lines l2->next, ... end. 910 * l2 should not be tkt->end. 911 * 912 * if finalwidth is 0, we're trying to work out what the 913 * width and height should be. if propagation is off, 914 * it's irrelevant; otherwise it must assume that 915 * its desired width will be fulfilled, as the packer 916 * doesn't iterate... 917 * 918 * N.B. this function rearranges lines, merges and splits items. 919 * this means that in general the item and line pointed to 920 * by any index might have been freed after tktfixgeom 921 * has been called. 922 */ 923 char* 924 tktfixgeom(Tk *tk, TkTline *l1, TkTline *l2, int finalwidth) 925 { 926 int x, y, a, wa, h, w, o, n, j, sp3, xleft, xright, winw, oa, oh, lh; 927 int wrapmode, just, needsplit; 928 char *e, *s; 929 TkText *tkt; 930 Tk *sub; 931 TkTitem *i, *it, *ilast, *iprev; 932 TkTindex ix, ixprev, ixw; 933 TkTline *l, *lafter; 934 Interval oldi, hole, rest, newrest; 935 TkEnv *env; 936 Font *f; 937 int *opts; 938 TkTtabstop *tb; 939 940 tkt = TKobj(TkText, tk); 941 942 if(tktdbg) 943 tktcheck(tkt, "tktfixgeom"); 944 945 if (!finalwidth && tkt->propagate == BoolT) { 946 if ((tk->flag & Tksetwidth) == 0) 947 winw = 1000000; 948 else 949 winw = tk->req.width; 950 } else { 951 winw = tk->act.width - tk->ipad.x; 952 if(winw <= 0) 953 winw = tk->req.width; 954 } 955 if(winw < 0) 956 return nil; 957 958 /* 959 * Make lafter be the first line after l2 that comes after a newline 960 * (so that wrap correction cannot affect it) 961 */ 962 lafter = l2->next; 963 if(tktdbg && lafter == nil) { 964 print("tktfixgeom: botch 1\n"); 965 return nil; 966 } 967 while((lafter->flags & TkTfirst) == 0 && lafter != &tkt->end) 968 lafter = lafter->next; 969 970 971 y = l1->orig.y + l1->height + tktpostspace(tk, l1); 972 973 oldi.lo = y; 974 oldi.hi = lafter->orig.y; 975 rest.lo = oldi.hi; 976 rest.hi = rest.lo + 1000; /* get background after end, too */ 977 978 opts = mallocz(TkTnumopts*sizeof(int), 0); 979 if(opts == nil) 980 return TkNomem; 981 env = mallocz(sizeof(TkEnv), 0); 982 if(env == nil) { 983 free(opts); 984 return TkNomem; 985 } 986 987 for(l = l1->next; l != lafter; l = l->next) { 988 if(tktdbg && l == nil) { 989 print("tktfixgeom: botch 2\n"); 990 free(opts); 991 free(env); 992 return nil; 993 } 994 995 l->flags &= ~TkTdrawn; 996 997 /* some spacing depends on tags of first non-mark on display line */ 998 iprev = nil; 999 for(i = l->items; i->kind == TkTmark; ) { 1000 iprev = i; 1001 i = i->next; 1002 } 1003 tkttagopts(tk, i, opts, env, &tb, 1); 1004 1005 if(l->flags&TkTfirst) { 1006 xleft = opts[TkTlmargin1]; 1007 y += opts[TkTspacing1]; 1008 } 1009 else { 1010 xleft = opts[TkTlmargin2]; 1011 y += opts[TkTspacing2]; 1012 } 1013 sp3 = opts[TkTspacing3]; 1014 just = opts[TkTjustify]; 1015 1016 wrapmode = opts[TkTwrap]; 1017 f = env->font; 1018 h = f->height; 1019 lh = opts[TkTlineheight]; 1020 a = f->ascent; 1021 x = xleft; 1022 xright = winw - opts[TkTrmargin]; 1023 if(xright < xleft) 1024 xright = xleft; 1025 1026 /* 1027 * perform line wrapping and calculate h (height) and a (ascent) 1028 * for the current line 1029 */ 1030 for(; i != nil; iprev = i, i = i->next) { 1031 again: 1032 if(i->kind == TkTmark) 1033 continue; 1034 if(i->kind == TkTnewline) 1035 break; 1036 if(i->kind == TkTcontline) { 1037 /* 1038 * See if some of following line fits on this one. 1039 * First, ensure that following line isn't empty. 1040 */ 1041 it = l->next->items; 1042 while(it->kind == TkTmark) 1043 it = it->next; 1044 1045 if(it->kind == TkTnewline || it->kind == TkTcontline) { 1046 /* next line is empty; join it to this one by removing i */ 1047 ix.item = i; 1048 ix.line = l; 1049 ix.pos = 0; 1050 tktremitem(tkt, &ix); 1051 it = l->next->items; 1052 if(iprev == nil) 1053 i = l->items; 1054 else 1055 i = iprev->next; 1056 goto again; 1057 } 1058 1059 n = xright - x; 1060 if(n <= 0) 1061 break; 1062 ixprev.line = l; 1063 ixprev.item = i; 1064 ixprev.pos = 0; 1065 ix = ixprev; 1066 tktadjustind(tkt, TkTbychar, &ix); 1067 if(wrapmode == Tkwrapword) 1068 tktadjustind(tkt, TkTbywrapend, &ix); 1069 if(wrapmode != Tkwrapnone && tktwidbetween(tk, x, &ixprev, &ix) > n) 1070 break; 1071 /* move one item up from next line and try again */ 1072 it = l->next->items; 1073 if(tktdbg && (it == nil || it->kind == TkTnewline || it->kind == TkTcontline)) { 1074 print("tktfixgeom: botch 3\n"); 1075 free(opts); 1076 free(env); 1077 return nil; 1078 } 1079 if(iprev == nil) 1080 l->items = it; 1081 else 1082 iprev->next = it; 1083 l->next->items = it->next; 1084 it->next = i; 1085 i = it; 1086 goto again; 1087 } 1088 1089 oa = a; 1090 oh = h; 1091 if(!tktanytags(i)) { 1092 env->font = tk->env->font; 1093 o = 0; 1094 } 1095 else { 1096 tkttagopts(tk, i, opts, env, nil, 1); 1097 o = opts[TkToffset]; 1098 } 1099 if((o != 0 || env->font != f) && i->kind != TkTwin) { 1100 /* check ascent of current item */ 1101 n = o+env->font->ascent; 1102 if(n > a) { 1103 a = n; 1104 h += (a - oa); 1105 } 1106 /* check descent of current item */ 1107 n = (env->font->height - env->font->ascent) - o; 1108 if(n > h-a) 1109 h = a + n; 1110 } 1111 if(i->kind == TkTwin && i->iwin->sub != nil) { 1112 sub = i->iwin->sub; 1113 n = 2 * i->iwin->pady + sub->act.height + 1114 2 * sub->borderwidth; 1115 switch(i->iwin->align) { 1116 case Tktop: 1117 case Tkbottom: 1118 if(n > h) 1119 h = n; 1120 break; 1121 case Tkcenter: 1122 if(n/2 > a) 1123 a = n/2; 1124 if(n/2 > h-a) 1125 h = a + n/2; 1126 break; 1127 case Tkbaseline: 1128 wa = i->iwin->ascent; 1129 if (wa == -1) 1130 wa = n; 1131 h = maximum(a, wa) + maximum(h - a, n - wa); 1132 a = maximum(a, wa); 1133 break; 1134 } 1135 } 1136 1137 w = tktdispwidth(tk, tb, i, env->font, x, 0, -1); 1138 n = x + w - xright; 1139 if(n > 0 && wrapmode != Tkwrapnone) { 1140 /* find shortest suffix that can be removed to fit item */ 1141 j = tktposcount(i) - 1; 1142 while(j > 0 && tktdispwidth(tk, tb, i, env->font, x, j, -1) < n) 1143 j--; 1144 /* put at least one item on a line before splitting */ 1145 if(j == 0 && x == xleft) { 1146 if(tktposcount(i) == 1) 1147 goto Nosplit; 1148 j = 1; 1149 } 1150 ix.line = l; 1151 ix.item = i; 1152 ix.pos = j; 1153 if(wrapmode == Tkwrapword) { 1154 /* trim the item at the first word at or before the shortest suffix */ 1155 /* TO DO: convert any resulting trailing white space to zero width */ 1156 ixw = ix; 1157 if(tktisbreak(tktindrune(&ixw))) { 1158 /* at break character, find end of word preceding it */ 1159 while(tktisbreak(tktindrune(&ixw))){ 1160 if(!tktadjustind(tkt, TkTbycharback, &ixw) || 1161 ixw.line != l || ixw.item == l->items && ixw.pos == 0) 1162 goto Wrapchar; /* no suitable point, degrade to char wrap */ 1163 } 1164 ix = ixw; 1165 } 1166 /* now find start of word */ 1167 tktadjustind(tkt, TkTbywrapstart, &ixw); 1168 if(ixw.line == l && (ixw.item != l->items || ixw.pos > 0)){ 1169 /* it will leave something on the line, so reasonable to split here */ 1170 ix = ixw; 1171 } 1172 /* otherwise degrade to char wrap */ 1173 } 1174 Wrapchar: 1175 if(ix.pos > 0) { 1176 needsplit = 1; 1177 e = tktsplititem(&ix); 1178 if(e != nil) { 1179 free(opts); 1180 free(env); 1181 return e; 1182 } 1183 } 1184 else 1185 needsplit = 0; 1186 1187 e = tktnewitem(TkTcontline, 0, &it); 1188 if(e != nil) { 1189 free(opts); 1190 free(env); 1191 return e; 1192 } 1193 e = tktiteminsert(tkt, &ix, it); 1194 if(e != nil) { 1195 tktfreeitems(tkt, it, 1); 1196 free(opts); 1197 free(env); 1198 return e; 1199 } 1200 1201 l = l->prev; /* work on part of line up to split */ 1202 1203 if(needsplit) { 1204 /* have to calculate width of pre-split part */ 1205 ixprev = ix; 1206 if(tktadjustind(tkt, TkTbyitemback, &ixprev) && 1207 tktadjustind(tkt, TkTbyitemback, &ixprev)) { 1208 w = tktdispwidth(tk, tb, ixprev.item, nil, x, 0, -1); 1209 ixprev.item->width = w; 1210 x += w; 1211 } 1212 } 1213 else { 1214 h = oh; 1215 a = oa; 1216 } 1217 break; 1218 } 1219 else { 1220 Nosplit: 1221 i->width =w; 1222 x += w; 1223 } 1224 } 1225 if (a > h) 1226 h = a; 1227 if (lh == 0) 1228 lh = f->height; 1229 if (lh > h) { 1230 a += (lh - h) / 2; 1231 h = lh; 1232 } 1233 1234 /* 1235 * Now line l is broken correctly and has correct item widths/line height/ascent. 1236 * Merge adjacent TkTascii/TkTrune items with same tags. 1237 * Also, set act{x,y} of embedded widgets to offset from 1238 * left of item box at baseline. 1239 */ 1240 for(i = l->items; i->next != nil; i = i->next) { 1241 it = i->next; 1242 if( (i->kind == TkTascii || i->kind == TkTrune) 1243 && 1244 i->kind == it->kind 1245 && 1246 tktsametags(i, it)) { 1247 n = strlen(i->istring); 1248 j = strlen(it->istring); 1249 s = realloc(i->istring, n + j + 1); 1250 if(s == nil) { 1251 free(opts); 1252 free(env); 1253 return TkNomem; 1254 } 1255 i->istring = s; 1256 memmove(i->istring+n, it->istring, j+1); 1257 i->width += it->width; 1258 i->next = it->next; 1259 it->next = nil; 1260 tktfreeitems(tkt, it, 1); 1261 } 1262 else if(i->kind == TkTwin && i->iwin->sub != nil) { 1263 sub = i->iwin->sub; 1264 n = sub->act.height + 2 * sub->borderwidth; 1265 o = i->iwin->pady; 1266 sub->act.x = i->iwin->padx; 1267 /* 1268 * sub->act.y is y-origin of widget relative to baseline. 1269 */ 1270 switch(i->iwin->align) { 1271 case Tktop: 1272 sub->act.y = o - a; 1273 break; 1274 case Tkbottom: 1275 sub->act.y = h - (o + n) - a; 1276 break; 1277 case Tkcenter: 1278 sub->act.y = (h - n) / 2 - a; 1279 break; 1280 case Tkbaseline: 1281 wa = i->iwin->ascent; 1282 if (wa == -1) 1283 wa = n; 1284 sub->act.y = -wa; 1285 break; 1286 } 1287 } 1288 } 1289 1290 l->width = x - xleft; 1291 1292 /* justification bug: wrong if line has tabs */ 1293 l->orig.x = xleft; 1294 n = xright - x; 1295 if(n > 0) { 1296 if(just == Tkright) 1297 l->orig.x += n; 1298 else 1299 if(just == Tkcenter) 1300 l->orig.x += n/2; 1301 } 1302 1303 /* give newline or contline width up to right margin */ 1304 ilast = tktlastitem(l->items); 1305 ilast->width = xright - l->width; 1306 if(ilast->width < 0) 1307 ilast->width = 0; 1308 1309 l->orig.y = y; 1310 l->height = h; 1311 l->ascent = a; 1312 y += h; 1313 if(l->flags&TkTlast) 1314 y += sp3; 1315 } 1316 free(opts); 1317 free(env); 1318 1319 tktdrawbg(tk, oldi.lo, oldi.hi, 0); 1320 1321 y += tktprespace(tk, l); 1322 newrest.lo = y; 1323 newrest.hi = y + rest.hi - rest.lo; 1324 1325 hole = tkttranslate(tk, newrest, rest.lo); 1326 1327 tktdrawbg(tk, hole.lo, hole.hi, 0); 1328 1329 if(l != &tkt->end) { 1330 while(l != &tkt->end) { 1331 oh = l->next->orig.y - l->orig.y; 1332 l->orig.y = y; 1333 if(y + oh > hole.lo && y < hole.hi) { 1334 l->flags &= ~TkTdrawn; 1335 } 1336 y += oh; 1337 l = l->next; 1338 } 1339 } 1340 tkt->end.orig.y = tkt->end.prev->orig.y + tkt->end.prev->height; 1341 1342 if(tkt->deltatv.y > tkt->end.orig.y) 1343 tkt->deltatv.y = tkt->end.prev->orig.y; 1344 1345 1346 e = tktsetscroll(tk, Tkvertical); 1347 if(e != nil) 1348 return e; 1349 e = tktsetscroll(tk, Tkhorizontal); 1350 if(e != nil) 1351 return e; 1352 1353 tk->dirty = tkrect(tk, 1); 1354 if(tktdbg) 1355 tktcheck(tkt, "tktfixgeom end"); 1356 return nil; 1357 } 1358 1359 static int 1360 tktpostspace(Tk *tk, TkTline *l) 1361 { 1362 int ans; 1363 TkTitem *i; 1364 TkEnv env; 1365 int *opts; 1366 1367 opts = mallocz(TkTnumopts*sizeof(int), 0); 1368 if(opts == nil) 1369 return 0; 1370 ans = 0; 1371 if(l->items != nil && (l->flags&TkTlast)) { 1372 for(i = l->items; i->kind == TkTmark; ) 1373 i = i->next; 1374 tkttagopts(tk, i, opts, &env, nil, 1); 1375 ans = opts[TkTspacing3]; 1376 } 1377 free(opts); 1378 return ans; 1379 } 1380 1381 static int 1382 tktprespace(Tk *tk, TkTline *l) 1383 { 1384 int ans; 1385 TkTitem *i; 1386 TkEnv env; 1387 int *opts; 1388 1389 opts = mallocz(TkTnumopts*sizeof(int), 0); 1390 if(opts == nil) 1391 return 0; 1392 1393 ans = 0; 1394 if(l->items != nil) { 1395 for(i = l->items; i->kind == TkTmark; ) 1396 i = i->next; 1397 tkttagopts(tk, i, opts, &env, nil, 1); 1398 if(l->flags&TkTfirst) 1399 ans = opts[TkTspacing1]; 1400 else 1401 ans = opts[TkTspacing2]; 1402 } 1403 free(opts); 1404 return ans; 1405 } 1406 1407 static int 1408 tktwidbetween(Tk *tk, int x, TkTindex *i1, TkTindex *i2) 1409 { 1410 int d, w, n; 1411 TkTindex ix; 1412 TkText *tkt = TKobj(TkText, tk); 1413 1414 w = 0; 1415 ix = *i1; 1416 while(ix.item != i2->item) { 1417 /* probably wrong w.r.t tag tabs */ 1418 d = tktdispwidth(tk, nil, ix.item, nil, x, ix.pos, -1); 1419 w += d; 1420 x += d; 1421 if(!tktadjustind(tkt, TkTbyitem, &ix)) { 1422 if(tktdbg) 1423 print("tktwidbetween botch\n"); 1424 break; 1425 } 1426 } 1427 n = i2->pos - ix.pos; 1428 if(n > 0) 1429 /* probably wrong w.r.t tag tabs */ 1430 w += tktdispwidth(tk, nil, ix.item, nil, x, ix.pos, i2->pos-ix.pos); 1431 return w; 1432 } 1433 1434 static Interval 1435 tktvclip(Interval i, int vh) 1436 { 1437 if(i.lo < 0) 1438 i.lo = 0; 1439 if(i.hi > vh) 1440 i.hi = vh; 1441 return i; 1442 } 1443 1444 /* 1445 * Do translation of any part of interval that appears on screen 1446 * starting at srcy to its new position, dsti. 1447 * Return y-range of the hole left in the image (either because 1448 * the src bits were out of the V window, or because the src bits 1449 * vacated an area of the V window). 1450 * The coordinates passed in and out are in T space. 1451 */ 1452 static Interval 1453 tkttranslate(Tk *tk, Interval dsti, int srcy) 1454 { 1455 int vh, vw, dvty, locked; 1456 TkText *tkt; 1457 Image *i; 1458 Interval hole, vdst, vsrc; 1459 Point src; 1460 Rectangle dst; 1461 Display *d; 1462 1463 hole.hi = 0; 1464 hole.lo = 0; 1465 1466 1467 /* 1468 * If we are embedded in a text widget, we need to come in through 1469 * the tkdrawtext routine, to ensure our clipr is set properly, so we 1470 * just punt in that case. 1471 * XXX is just checking parent good enough. what if we're in 1472 * a frame in a text widget? 1473 * BUG! 1474 1475 * if(tk->parent != nil && tk->parent->type == TKtext) { 1476 * tk->flag |= Tkrefresh; 1477 * return hole; 1478 * } 1479 */ 1480 tkt = TKobj(TkText, tk); 1481 dvty = tkt->deltatv.y; 1482 i = tkt->image; 1483 1484 vw = tk->act.width - tk->ipad.x; 1485 vh = tk->act.height - tk->ipad.y; 1486 1487 /* convert to V space */ 1488 vdst.lo = dsti.lo - dvty; 1489 vdst.hi = dsti.hi - dvty; 1490 vsrc.lo = srcy - dvty; 1491 vsrc.hi = vsrc.lo + dsti.hi - dsti.lo; 1492 if(vsrc.lo == vsrc.hi || vsrc.lo == vdst.lo) 1493 return hole; 1494 else if(vsrc.hi <= 0 || vsrc.lo >= vh) 1495 hole = tktvclip(vdst, vh); 1496 else if(vdst.hi <= 0 || vdst.lo >= vh) 1497 hole = tktvclip(vsrc, vh); 1498 else if(i != nil) { 1499 src.x = 0; 1500 src.y = vsrc.lo; 1501 if(vdst.lo > vsrc.lo) { /* see earlier text lines */ 1502 if(vsrc.lo < 0) { 1503 src.y = 0; 1504 vdst.lo -= vsrc.lo; 1505 } 1506 if(vdst.hi > vh) 1507 vdst.hi = vh; 1508 hole.lo = src.y; 1509 hole.hi = vdst.lo; 1510 } 1511 else { /* see later text lines */ 1512 if(vsrc.hi > vh) 1513 vdst.hi -= (vsrc.hi - vh); 1514 if(vdst.lo < 0){ 1515 src.y -= vdst.lo; 1516 vdst.lo = 0; 1517 } 1518 hole.lo = vdst.hi; 1519 hole.hi = src.y + (vdst.hi - vdst.lo); 1520 } 1521 if(vdst.hi > vdst.lo && (tkt->tflag&TkTdrawn)) { 1522 src = addpt(src, tkt->deltaiv); 1523 dst = rectaddpt(Rect(0, vdst.lo, vw, vdst.hi), tkt->deltaiv); 1524 d = tk->env->top->display; 1525 locked = 0; 1526 if(!(tkt->tflag&TkTdlocked)) 1527 locked = lockdisplay(d); 1528 i = tkimageof(tk); 1529 tkt->image = i; 1530 if(i != nil) 1531 draw(i, dst, i, nil, src); 1532 if(locked) 1533 unlockdisplay(d); 1534 } 1535 } 1536 hole.lo += dvty; 1537 hole.hi += dvty; 1538 return hole; 1539 } 1540 1541 /* 1542 * mark lines from firsty to lasty as not drawn. 1543 * firsty and lasty are in T space 1544 */ 1545 static void 1546 tktnotdrawn(Tk *tk, int firsty, int lasty, int all) 1547 { 1548 TkTline *lend, *l; 1549 TkText *tkt = TKobj(TkText, tk); 1550 if(firsty >= lasty && !all) 1551 return; 1552 lend = &tkt->end; 1553 for(l = tkt->start.next; l != lend; l = l->next) { 1554 if(l->orig.y+l->height <= firsty) 1555 continue; 1556 if(l->orig.y >= lasty) 1557 break; 1558 l->flags &= ~TkTdrawn; 1559 if (firsty > l->orig.y) 1560 firsty = l->orig.y; 1561 if (lasty < l->orig.y+l->height) 1562 lasty = l->orig.y+l->height; 1563 } 1564 tktdrawbg(tk, firsty, lasty, all); 1565 tk->dirty = tkrect(tk, 1); 1566 } 1567 1568 /* 1569 * firsty and lasty are in T space 1570 */ 1571 static void 1572 tktdrawbg(Tk *tk, int firsty, int lasty, int all) 1573 { 1574 int vw, vh, locked; 1575 Rectangle r; 1576 Image *i; 1577 Display *d; 1578 TkText *tkt = TKobj(TkText, tk); 1579 1580 if(tk->env->top->root->flag & Tksuspended){ 1581 tk->flag |= Tkrefresh; 1582 return; 1583 } 1584 /* 1585 * If we are embedded in a text widget, we need to come in through 1586 * the tkdrawtext routine, to ensure our clipr is set properly, so we 1587 * just punt in that case. 1588 * BUG! 1589 * if(tk->parent != nil && tk->parent->type == TKtext) { 1590 * tk->flag |= Tkrefresh; 1591 * return; 1592 * } 1593 */ 1594 vw = tk->act.width - tk->ipad.x; 1595 vh = tk->act.height - tk->ipad.y; 1596 if(all) { 1597 /* whole background is to be drawn, not just until last line */ 1598 firsty = 0; 1599 lasty = 100000; 1600 } 1601 if(firsty >= lasty) 1602 return; 1603 firsty -= tkt->deltatv.y; 1604 lasty -= tkt->deltatv.y; 1605 if(firsty < 0) 1606 firsty = 0; 1607 if(lasty > vh) 1608 lasty = vh; 1609 r = rectaddpt(Rect(0, firsty, vw, lasty), tkt->deltaiv); 1610 if(r.min.y < r.max.y && (tkt->tflag&TkTdrawn)) { 1611 d = tk->env->top->display; 1612 locked = 0; 1613 if(!(tkt->tflag&TkTdlocked)) 1614 locked = lockdisplay(d); 1615 i = tkimageof(tk); 1616 tkt->image = i; 1617 if(i != nil) 1618 draw(i, r, tkgc(tk->env, TkCbackgnd), nil, ZP); 1619 if(locked) 1620 unlockdisplay(d); 1621 } 1622 } 1623 1624 static void 1625 tktfixscroll(Tk *tk, Point odeltatv) 1626 { 1627 int lasty; 1628 Interval oi, hole; 1629 Rectangle oclipr; 1630 Image *dst; 1631 Point ndeltatv; 1632 TkText *tkt = TKobj(TkText, tk); 1633 1634 ndeltatv = tkt->deltatv; 1635 1636 if(eqpt(odeltatv, ndeltatv)) 1637 return; 1638 1639 /* set clipr to avoid spilling outside (in case didn't come in through draw) */ 1640 dst = tkimageof(tk); 1641 if(dst != nil) { 1642 tkt->image = dst; 1643 oclipr = dst->clipr; 1644 tktsetclip(tk); 1645 } 1646 1647 lasty = tkt->end.orig.y; 1648 if(odeltatv.x != ndeltatv.x) 1649 tktnotdrawn(tk, ndeltatv.y, lasty, 0); 1650 else { 1651 oi.lo = odeltatv.y; 1652 oi.hi = lasty; 1653 hole = tkttranslate(tk, oi, ndeltatv.y); 1654 tktnotdrawn(tk, hole.lo, hole.hi, 0); 1655 } 1656 if(dst != nil) 1657 tktreplclipr(dst, oclipr); 1658 } 1659 1660 void 1661 tktextgeom(Tk *tk) 1662 { 1663 TkTindex ix; 1664 Rectangle oclipr; 1665 Image *dst; 1666 TkText *tkt = TKobj(TkText, tk); 1667 char buf[20], *p; 1668 1669 tkt->tflag &= ~TkTdrawn; 1670 tktsetdeltas(tk, ZP); 1671 /* find index of current top-left, so can see it again */ 1672 tktxyind(tk, 0, 0, &ix); 1673 /* make sure scroll bar is redrawn */ 1674 tkt->scrolltop[Tkvertical] = -1; 1675 tkt->scrolltop[Tkhorizontal] = -1; 1676 tkt->scrollbot[Tkvertical] = -1; 1677 tkt->scrollbot[Tkhorizontal] = -1; 1678 1679 /* set clipr to avoid spilling outside (didn't come in through draw) */ 1680 dst = tkimageof(tk); 1681 if(dst != nil) { 1682 tkt->image = dst; 1683 oclipr = dst->clipr; 1684 tktsetclip(tk); 1685 } 1686 1687 /* 1688 * have to save index in a reusable format, as 1689 * tktfixgeom can free everything that ix points to. 1690 */ 1691 snprint(buf, sizeof(buf), "%d.%d", tktlinenum(tkt, &ix), tktlinepos(tkt, &ix)); 1692 tktfixgeom(tk, &tkt->start, tkt->end.prev, 1); 1693 p = buf; 1694 tktindparse(tk, &p, &ix); /* restore index to something close to original value */ 1695 tktsee(tk, &ix, 1); 1696 1697 if(dst != nil) 1698 tktreplclipr(dst, oclipr); 1699 } 1700 1701 static char* 1702 tktsetscroll(Tk *tk, int orient) 1703 { 1704 TkText *tkt; 1705 TkTline *l; 1706 int ntot, nmin, nmax, top, bot, vw, vh; 1707 char *val, *cmd, *v, *e, *s; 1708 1709 tkt = TKobj(TkText, tk); 1710 1711 s = (orient == Tkvertical)? tkt->yscroll : tkt->xscroll; 1712 if(s == nil) 1713 return nil; 1714 1715 vw = tk->act.width - tk->ipad.x; 1716 vh = tk->act.height - tk->ipad.y; 1717 1718 if(orient == Tkvertical) { 1719 l = tkt->end.prev; 1720 ntot = l->orig.y + l->height; 1721 nmin = tkt->deltatv.y; 1722 if(vh <= 0) 1723 nmax = nmin; 1724 else 1725 nmax = nmin + vh; 1726 } 1727 else { 1728 ntot = tktmaxwid(tkt->start.next); 1729 nmin = tkt->deltatv.x; 1730 if(vw <= 0) 1731 nmax = nmin; 1732 else 1733 nmax = nmin + vw; 1734 } 1735 1736 if(ntot == 0) { 1737 top = 0; 1738 bot = TKI2F(1); 1739 } 1740 else { 1741 if(ntot < nmax) 1742 ntot = nmax; 1743 top = TKI2F(nmin)/ntot; 1744 bot = TKI2F(nmax)/ntot; 1745 } 1746 1747 if(tkt->scrolltop[orient] == top && tkt->scrollbot[orient] == bot) 1748 return nil; 1749 1750 tkt->scrolltop[orient] = top; 1751 tkt->scrollbot[orient] = bot; 1752 1753 val = mallocz(Tkminitem, 0); 1754 if(val == nil) 1755 return TkNomem; 1756 cmd = mallocz(Tkmaxitem, 0); 1757 if(cmd == nil) { 1758 free(val); 1759 return TkNomem; 1760 } 1761 1762 v = tkfprint(val, top); 1763 *v++ = ' '; 1764 tkfprint(v, bot); 1765 snprint(cmd, Tkmaxitem, "%s %s", s, val); 1766 e = tkexec(tk->env->top, cmd, nil); 1767 free(cmd); 1768 free(val); 1769 return e; 1770 } 1771 1772 static char* 1773 tktview(Tk *tk, char *arg, char **val, int nl, int *posn, int max, int orient) 1774 { 1775 int top, bot, amount, n; 1776 char buf[Tkminitem], *v, *e; 1777 1778 if(*arg == '\0') { 1779 if ( max == 0 ) { 1780 top = 0; 1781 bot = TKI2F(1); 1782 } 1783 else { 1784 top = TKI2F(*posn)/max; 1785 bot = TKI2F(*posn+nl)/max; 1786 if (bot > TKI2F(1)) 1787 bot = TKI2F(1); 1788 } 1789 v = tkfprint(buf, top); 1790 *v++ = ' '; 1791 tkfprint(v, bot); 1792 return tkvalue(val, "%s", buf); 1793 } 1794 1795 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 1796 if(strcmp(buf, "moveto") == 0) { 1797 e = tkfracword(tk->env->top, &arg, &top, nil); 1798 if (e != nil) 1799 return e; 1800 *posn = TKF2I(top*max); 1801 } 1802 else 1803 if(strcmp(buf, "scroll") == 0) { 1804 e = tkfracword(tk->env->top, &arg, &amount, nil); 1805 if(e != nil) 1806 return e; 1807 arg = tkskip(arg, " \t"); 1808 if(*arg == 'p') /* Pages */ 1809 amount *= nl; 1810 else /* Lines or Characters */ 1811 if(orient == Tkvertical) { 1812 /* XXX needs improvement */ 1813 amount *= tk->env->font->height; 1814 } 1815 else 1816 amount *= tk->env->wzero; 1817 amount = TKF2I(amount); 1818 n = *posn + amount; 1819 if(n < 0) 1820 n = 0; 1821 if(n > max) 1822 n = max; 1823 *posn = n; 1824 } 1825 else 1826 return TkBadcm; 1827 1828 bot = max - (nl * 3 / 4); 1829 if(*posn > bot) 1830 *posn = bot; 1831 if(*posn < 0) 1832 *posn = 0; 1833 1834 return nil; 1835 } 1836 1837 static void 1838 tktclearsel(Tk *tk) 1839 { 1840 TkTindex ibeg, iend; 1841 TkText *tkt = TKobj(TkText, tk); 1842 1843 if(tkt->selfirst == nil) 1844 return; 1845 tktitemind(tkt->selfirst, &ibeg); 1846 tktitemind(tkt->sellast, &iend); 1847 1848 tkttagchange(tk, TkTselid, &ibeg, &iend, 0); 1849 } 1850 1851 static int 1852 tktgetsel(Tk *tk, TkTindex *i1, TkTindex *i2) 1853 { 1854 TkText *tkt =TKobj(TkText, tk); 1855 1856 if(tkt->selfirst == nil) 1857 return 0; 1858 tktitemind(tkt->selfirst, i1); 1859 tktitemind(tkt->sellast, i2); 1860 return 1; 1861 } 1862 1863 /* 1864 * Adjust tkt->deltatv so that indexed character is visible. 1865 * - if seetop is true, make indexed char be at top of window 1866 * - if it is already visible, do nothing. 1867 * - if it is > 1/2 screenful off edge of screen, center it 1868 * else put it at bottom or top (whichever is nearer) 1869 * - if first line is visible, put it at top 1870 * - if last line is visible, allow one blank line at bottom 1871 * 1872 * BUG: should handle x visibility too 1873 */ 1874 static void 1875 tktsee(Tk *tk, TkTindex *ixp, int seetop) 1876 { 1877 int ycur, ynext, deltatvy, adjy, h; 1878 Point p, odeltatv; 1879 Rectangle bbox; 1880 TkTline *l, *el; 1881 TkText *tkt = TKobj(TkText, tk); 1882 TkTindex ix; 1883 1884 ix = *ixp; 1885 deltatvy = tkt->deltatv.y; 1886 odeltatv = tkt->deltatv; 1887 h = tk->act.height; 1888 1889 /* find p (in T space): top left of indexed line */ 1890 l = ix.line; 1891 p = l->orig; 1892 1893 /* ycur, ynext in V space */ 1894 ycur = p.y - deltatvy; 1895 ynext = ycur + l->height; 1896 adjy = 0; 1897 1898 /* quantize h to line boundaries (works if single font) */ 1899 if ( l->height ) 1900 h -= h%l->height; 1901 1902 if(seetop) { 1903 deltatvy = p.y; 1904 adjy = 1; 1905 } 1906 else 1907 if(ycur < 0 || ynext >= h) { 1908 adjy = 1; 1909 1910 if(ycur < -h/2 || ycur > 3*h/2) 1911 deltatvy = p.y - h/2; 1912 else if(ycur < 0) 1913 deltatvy = p.y; 1914 else 1915 deltatvy = p.y - h + l->height; 1916 1917 el = tkt->end.prev; 1918 if(el != nil && el->orig.y - deltatvy < h) 1919 deltatvy = tkt->end.orig.y - (h * 3 / 4); 1920 1921 if(p.y - deltatvy < 0) 1922 deltatvy = p.y; 1923 if(deltatvy < 0) 1924 deltatvy = 0; 1925 } 1926 if(adjy) { 1927 tkt->deltatv.y = deltatvy; 1928 tktsetscroll(tk, Tkvertical); /* XXX - Tad: err ignored */ 1929 tktfixscroll(tk, odeltatv); 1930 } 1931 while (ix.item->kind == TkTmark) 1932 ix.item = ix.item->next; 1933 bbox = tktbbox(tk, &ix); 1934 /* make sure that cursor at the end gets shown */ 1935 tksee(tk, bbox, Pt(bbox.min.x, (bbox.min.y + bbox.max.y) / 2)); 1936 } 1937 1938 static int 1939 tktcmatch(int c1, int c2, int nocase) 1940 { 1941 if(nocase) { 1942 if(c1 >= 'a' && c1 <= 'z') 1943 c1 -= 'a' - 'A'; 1944 if(c2 >= 'a' && c2 <= 'z') 1945 c2 -= 'a' - 'A'; 1946 } 1947 return (c1 == c2); 1948 } 1949 1950 /* 1951 * Return 1 if tag with id m1 ends before tag with id m2, 1952 * starting at the item after that indexed in ix (but don't 1953 * modify ix). 1954 */ 1955 static int 1956 tagendsbefore(TkText *tkt, TkTindex *ix, int m1, int m2) 1957 { 1958 int s1, s2; 1959 TkTindex ix1; 1960 TkTitem *i; 1961 1962 ix1 = *ix; 1963 while(tktadjustind(tkt, TkTbyitem, &ix1)) { 1964 i = ix1.item; 1965 if(i->kind == TkTwin || i->kind == TkTcontline || i->kind == TkTmark) 1966 continue; 1967 s1 = tkttagset(i, m1); 1968 s2 = tkttagset(i, m2); 1969 if(!s1) 1970 return s2; 1971 else if(!s2) 1972 return 0; 1973 } 1974 return 0; 1975 } 1976 1977 static int 1978 tktsgmltags(TkText *tkt, Fmt *fmt, TkTitem *iprev, TkTitem *i, TkTindex *ix, int *stack, int *pnstack, int *tmpstack) 1979 { 1980 int nprev, n, m, r, k, j, ii, onstack, nt; 1981 1982 nprev = 0; 1983 if(iprev != nil && (iprev->tags[0] != 0 || iprev->tagextra > 0)) 1984 nprev = 32*(iprev->tagextra + 1); 1985 n = 0; 1986 if(i != nil && (i->tags[0] != 0 || i->tagextra > 0)) 1987 n = 32*(i->tagextra + 1); 1988 nt = 0; 1989 if(n > 0) { 1990 /* find tags which open here */ 1991 for(m = 0; m < n; m++) 1992 if(tkttagset(i, m) && (iprev == nil || !tkttagset(iprev, m))) 1993 tmpstack[nt++] = m; 1994 } 1995 if(nprev > 0) { 1996 /* 1997 * Find lowest tag in stack that ends before any tag beginning here. 1998 * We have to emit end tags all the way down to there, then add 1999 * back the ones that haven't actually ended here, together with ones 2000 * that start here, and sort all of the added ones so that tags that 2001 * end later are lower in the stack. 2002 */ 2003 ii = *pnstack; 2004 for(k = *pnstack - 1; k >=0; k--) { 2005 m = stack[k]; 2006 if(i == nil || !tkttagset(i, m)) 2007 ii = k; 2008 else 2009 for(j = 0; j < nt; j++) 2010 if(tagendsbefore(tkt, ix, m, tmpstack[j])) 2011 ii = k; 2012 } 2013 for(k = *pnstack - 1; k >= ii; k--) { 2014 m = stack[k]; 2015 r = fmtprint(fmt, "</%s>", tkttagname(tkt, m)); 2016 if(r < 0) 2017 return r; 2018 /* add m back to starting tags if m didn't actually end here */ 2019 if(i != nil && tkttagset(i, m)) 2020 tmpstack[nt++] = m; 2021 } 2022 *pnstack = ii; 2023 } 2024 if(nt > 0) { 2025 /* add tags which open or reopen here */ 2026 onstack = *pnstack; 2027 k = onstack; 2028 for(j = 0; j < nt; j++) 2029 stack[k++] = tmpstack[j]; 2030 *pnstack = k; 2031 if(k - onstack > 1) { 2032 /* sort new stack entries so tags that end later are lower in stack */ 2033 for(ii = k-2; ii>= onstack; ii--) { 2034 m = stack[ii]; 2035 for(j = ii+1; j < k && tagendsbefore(tkt, ix, m, stack[j]); j++) { 2036 stack[j-1] = stack[j]; 2037 } 2038 stack[j-1] = m; 2039 } 2040 } 2041 for(j = onstack; j < k; j++) { 2042 r = fmtprint(fmt, "<%s>", tkttagname(tkt, stack[j])); 2043 if(r < 0) 2044 return r; 2045 } 2046 } 2047 return 0; 2048 } 2049 2050 /* 2051 * In 'sgml' format, just print text (no special treatment of 2052 * special characters, except that < turns into <) 2053 * interspersed with things like <Bold> and </Bold> 2054 * (where Bold is a tag name). 2055 * Make sure that the tag pairs nest properly. 2056 */ 2057 static char* 2058 tktget(TkText *tkt, TkTindex *ix1, TkTindex *ix2, int sgml, char **val) 2059 { 2060 int n, m, i, bychar, nstack; 2061 int *stack, *tmpstack; 2062 char *s; 2063 TkTitem *iprev; 2064 Tk *sub; 2065 Fmt fmt; 2066 char *buf; 2067 2068 if(!tktindbefore(ix1, ix2)) 2069 return nil; 2070 2071 stack = nil; 2072 tmpstack = nil; 2073 2074 iprev = nil; 2075 fmtstrinit(&fmt); 2076 buf = mallocz(100, 0); 2077 if(buf == nil) 2078 return TkNomem; 2079 if(sgml) { 2080 stack = malloc((tkt->nexttag+1)*sizeof(int)); 2081 tmpstack = malloc((tkt->nexttag+1)*sizeof(int)); 2082 if(stack == nil || tmpstack == nil) 2083 goto nomemret; 2084 nstack = 0; 2085 } 2086 for(;;) { 2087 if(ix1->item == ix2->item && ix1->pos == ix2->pos) 2088 break; 2089 s = nil; 2090 bychar = 0; 2091 m = 1; 2092 switch(ix1->item->kind) { 2093 case TkTrune: 2094 s = ix1->item->istring; 2095 s += tktutfpos(s, ix1->pos); 2096 if(ix1->item == ix2->item) { 2097 m = ix2->pos - ix1->pos; 2098 bychar = 1; 2099 } 2100 break; 2101 case TkTascii: 2102 s = ix1->item->istring + ix1->pos; 2103 if(ix1->item == ix2->item) { 2104 m = ix2->pos - ix1->pos; 2105 bychar = 1; 2106 } 2107 else { 2108 m = strlen(s); 2109 if(sgml && memchr(s, '<', m) != nil) 2110 bychar = 1; 2111 } 2112 break; 2113 case TkTtab: 2114 s = "\t"; 2115 break; 2116 case TkTnewline: 2117 s = "\n"; 2118 break; 2119 case TkTwin: 2120 sub = ix1->item->iwin->sub; 2121 if(sgml && sub != nil && sub->name != nil) { 2122 snprint(buf, 100, "<Window %s>", sub->name->name); 2123 s = buf; 2124 } 2125 } 2126 if(s != nil) { 2127 if(sgml) { 2128 n = tktsgmltags(tkt, &fmt, iprev, ix1->item, ix1, stack, &nstack, tmpstack); 2129 if(n < 0) 2130 goto nomemret; 2131 } 2132 if(bychar) { 2133 if (ix1->item->kind == TkTrune) 2134 n = fmtprint(&fmt, "%.*s", m, s); 2135 else { 2136 n = 0; 2137 for(i = 0; i < m && n >= 0; i++) { 2138 if(s[i] == '<') 2139 n = fmtprint(&fmt, "<"); 2140 else 2141 n = fmtprint(&fmt, "%c", s[i]); 2142 } 2143 } 2144 } 2145 else 2146 n = fmtprint(&fmt, "%s", s); 2147 if(n < 0) 2148 goto nomemret; 2149 iprev = ix1->item; 2150 } 2151 if(ix1->item == ix2->item) 2152 break; 2153 if(!tktadjustind(tkt, TkTbyitem, ix1)) { 2154 if(tktdbg) 2155 print("tktextget botch\n"); 2156 break; 2157 } 2158 } 2159 if(sgml) { 2160 n = tktsgmltags(tkt, &fmt, iprev, nil, nil, stack, &nstack, tmpstack); 2161 if(n < 0) 2162 goto nomemret; 2163 } 2164 2165 *val = fmtstrflush(&fmt); 2166 free(buf); 2167 return nil; 2168 2169 nomemret: 2170 free(buf); 2171 if(stack != nil) 2172 free(stack); 2173 if(tmpstack != nil) 2174 free(tmpstack); 2175 return TkNomem; 2176 } 2177 2178 /* Widget Commands (+ means implemented) 2179 +bbox 2180 +cget 2181 +compare 2182 +configure 2183 +debug 2184 +delete 2185 +dlineinfo 2186 +dump 2187 +get 2188 +index 2189 +insert 2190 +mark 2191 +scan 2192 +search 2193 +see 2194 +tag 2195 +window 2196 +xview 2197 +yview 2198 */ 2199 2200 static int 2201 tktviewrectclip(Rectangle *r, Rectangle b); 2202 2203 static char* 2204 tktextbbox(Tk *tk, char *arg, char **val) 2205 { 2206 char *e; 2207 int noclip, w, h; 2208 Rectangle r, rview; 2209 TkTindex ix; 2210 TkText *tkt; 2211 char buf[Tkmaxitem]; 2212 2213 e = tktindparse(tk, &arg, &ix); 2214 if(e != nil) 2215 return e; 2216 2217 noclip = 0; 2218 if(*arg != '\0') { 2219 /* extension to tk4.0: 2220 * "noclip" means don't clip to viewable area 2221 * "all" means give unclipped bbox of entire contents 2222 */ 2223 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 2224 if(strcmp(buf, "noclip") == 0) 2225 noclip = 1; 2226 else 2227 if(strcmp(buf, "all") == 0) { 2228 tkt = TKobj(TkText, tk); 2229 w = tktmaxwid(tkt->start.next); 2230 h = tkt->end.orig.y; 2231 return tkvalue(val, "0 0 %d %d", w, h); 2232 } 2233 } 2234 2235 /* 2236 * skip marks; bbox applies to characters only. 2237 * it's not defined what happens when bbox is applied to a newline char, 2238 * so we'll just let the default case sort that out. 2239 */ 2240 while (ix.item->kind == TkTmark) 2241 ix.item = ix.item->next; 2242 r = tktbbox(tk, &ix); 2243 2244 rview.min.x = 0; 2245 rview.min.y = 0; 2246 rview.max.x = tk->act.width - tk->ipad.x; 2247 rview.max.y = tk->act.height - tk->ipad.y; 2248 if(noclip || tktviewrectclip(&r, rview)) 2249 return tkvalue(val, "%d %d %d %d", r.min.x, r.min.y, 2250 r.max.x-r.min.x, r.max.y-r.min.y); 2251 return nil; 2252 } 2253 2254 /* 2255 * a supplemented rectclip, as ((0, 1), (0,1)) does not intersect ((0, 0), (5, 5)) 2256 * but for our purposes, we want it to. it's a hack. 2257 */ 2258 static int 2259 tktviewrectclip(Rectangle *rp, Rectangle b) 2260 { 2261 Rectangle *bp = &b; 2262 if((rp->min.x<bp->max.x && 2263 (bp->min.x<rp->max.x || (rp->max.x == b.min.x 2264 && rp->min.x == b.min.x)) && 2265 rp->min.y<bp->max.y && bp->min.y<rp->max.y)==0) 2266 return 0; 2267 /* They must overlap */ 2268 if(rp->min.x < bp->min.x) 2269 rp->min.x = bp->min.x; 2270 if(rp->min.y < bp->min.y) 2271 rp->min.y = bp->min.y; 2272 if(rp->max.x > bp->max.x) 2273 rp->max.x = bp->max.x; 2274 if(rp->max.y > bp->max.y) 2275 rp->max.y = bp->max.y; 2276 return 1; 2277 } 2278 2279 static Point 2280 scr2local(Tk *tk, Point p) 2281 { 2282 p = subpt(p, tkposn(tk)); 2283 p.x -= tk->borderwidth; 2284 p.y -= tk->borderwidth; 2285 return p; 2286 } 2287 2288 static char* 2289 tktextbutton1(Tk *tk, char *arg, char **val) 2290 { 2291 char *e; 2292 Point p; 2293 TkCtxt *c; 2294 TkTindex ix; 2295 TkTmarkinfo *mi; 2296 TkText *tkt = TKobj(TkText, tk); 2297 2298 USED(val); 2299 2300 e = tkxyparse(tk, &arg, &p); 2301 if(e != nil) 2302 return e; 2303 tkt->track = p; 2304 p = scr2local(tk, p); 2305 2306 tktxyind(tk, p.x, p.y, &ix); 2307 tkt->tflag &= ~TkTjustfoc; 2308 c = tk->env->top->ctxt; 2309 if(!(tk->flag&Tkdisabled) && c->tkkeygrab != tk 2310 && (tk->name != nil) && ix.item->kind != TkTwin) { 2311 tkfocus(tk->env->top, tk->name->name, nil); 2312 tkt->tflag |= TkTjustfoc; 2313 return nil; 2314 } 2315 2316 mi = tktfindmark(tkt->marks, "insert"); 2317 if(tktdbg && !mi) { 2318 print("tktextbutton1: botch\n"); 2319 return nil; 2320 } 2321 tktmarkmove(tk, mi, &ix); 2322 2323 tktclearsel(tk); 2324 tkrepeat(tk, autoselect, nil, TkRptpause, TkRptinterval); 2325 return nil; 2326 } 2327 2328 static char* 2329 tktextbutton1r(Tk *tk, char *arg, char **val) 2330 { 2331 TkText *tkt; 2332 2333 USED(arg); 2334 USED(val); 2335 2336 tkt = TKobj(TkText, tk); 2337 tkt->tflag &= ~TkTnodrag; 2338 tkcancelrepeat(tk); 2339 return nil; 2340 } 2341 2342 static char* 2343 tktextcget(Tk *tk, char *arg, char **val) 2344 { 2345 TkText *tkt; 2346 TkOptab tko[3]; 2347 2348 tkt = TKobj(TkText, tk); 2349 tko[0].ptr = tk; 2350 tko[0].optab = tkgeneric; 2351 tko[1].ptr = tkt; 2352 tko[1].optab = textopts; 2353 tko[2].ptr = nil; 2354 2355 return tkgencget(tko, arg, val, tk->env->top); 2356 } 2357 2358 static char* 2359 tktextcompare(Tk *tk, char *arg, char **val) 2360 { 2361 int op; 2362 char *e; 2363 TkTindex i1, i2; 2364 TkText *tkt; 2365 TkStab *s; 2366 char *buf; 2367 2368 tkt = TKobj(TkText, tk); 2369 2370 e = tktindparse(tk, &arg, &i1); 2371 if(e != nil) 2372 return e; 2373 2374 if(*arg == '\0') 2375 return TkBadcm; 2376 2377 buf = mallocz(Tkmaxitem, 0); 2378 if(buf == nil) 2379 return TkNomem; 2380 2381 arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); 2382 2383 op = -1; 2384 for(s = tkcompare; s->val; s++) 2385 if(strcmp(s->val, buf) == 0) { 2386 op = s->con; 2387 break; 2388 } 2389 if(op == -1) { 2390 free(buf); 2391 return TkBadcm; 2392 } 2393 2394 e = tktindparse(tk, &arg, &i2); 2395 if(e != nil) { 2396 free(buf); 2397 return e; 2398 } 2399 2400 e = tkvalue(val, tktindcompare(tkt, &i1, op, &i2)? "1" : "0"); 2401 free(buf); 2402 return e; 2403 } 2404 2405 static char* 2406 tktextconfigure(Tk *tk, char *arg, char **val) 2407 { 2408 char *e; 2409 TkGeom g; 2410 int bd; 2411 TkText *tkt; 2412 TkOptab tko[3]; 2413 tkt = TKobj(TkText, tk); 2414 tko[0].ptr = tk; 2415 tko[0].optab = tkgeneric; 2416 tko[1].ptr = tkt; 2417 tko[1].optab = textopts; 2418 tko[2].ptr = nil; 2419 2420 if(*arg == '\0') 2421 return tkconflist(tko, val); 2422 2423 g = tk->req; 2424 bd = tk->borderwidth; 2425 2426 e = tkparse(tk->env->top, arg, tko, nil); 2427 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 2428 if (tkt->propagate != BoolT) { 2429 if ((tk->flag & Tksetwidth) == 0) 2430 tk->req.width = tk->env->wzero*Textwidth; 2431 if ((tk->flag & Tksetheight) == 0) 2432 tk->req.height = tk->env->font->height*Textheight; 2433 } 2434 /* note: tkgeomchg() may also call tktfixgeom() via tktextgeom() */ 2435 tktfixgeom(tk, &tkt->start, tkt->end.prev, 0); 2436 tktextsize(tk, 0); 2437 tkgeomchg(tk, &g, bd); 2438 tktnotdrawn(tk, 0, tkt->end.orig.y, 1); 2439 2440 return e; 2441 } 2442 2443 static char* 2444 tktextdebug(Tk *tk, char *arg, char **val) 2445 { 2446 char buf[Tkmaxitem]; 2447 2448 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 2449 if(*buf == '\0') 2450 return tkvalue(val, "%s", tktdbg? "on" : "off"); 2451 else { 2452 tktdbg = (strcmp(buf, "1") == 0 || strcmp(buf, "yes") == 0); 2453 if(tktdbg) { 2454 tktprinttext(TKobj(TkText, tk)); 2455 } 2456 return nil; 2457 } 2458 } 2459 2460 static char* 2461 tktextdelete(Tk *tk, char *arg, char **val) 2462 { 2463 int sameit; 2464 char *e; 2465 TkTindex i1, i2, ip, isee; 2466 TkTline *lmin; 2467 TkText *tkt = TKobj(TkText, tk); 2468 char buf[20], *p; 2469 2470 USED(val); 2471 2472 e = tktindparse(tk, &arg, &i1); 2473 if(e != nil) 2474 return e; 2475 tktadjustind(tkt, TkTbycharstart, &i1); 2476 2477 e = tktsplititem(&i1); 2478 if(e != nil) 2479 return e; 2480 2481 if(*arg != '\0') { 2482 e = tktindparse(tk, &arg, &i2); 2483 if(e != nil) 2484 return e; 2485 } 2486 else { 2487 i2 = i1; 2488 tktadjustind(tkt, TkTbychar, &i2); 2489 } 2490 if(tktindcompare(tkt, &i1, TkGte, &i2)) 2491 return nil; 2492 2493 sameit = (i1.item == i2.item); 2494 2495 /* save possible fixup see place */ 2496 isee.line = nil; 2497 if(i2.line->orig.y + i2.line->height < tkt->deltatv.y) { 2498 /* delete completely precedes view */ 2499 tktxyind(tk, 0, 0, &isee); 2500 } 2501 2502 e = tktsplititem(&i2); 2503 if(e != nil) 2504 return e; 2505 2506 if(sameit) { 2507 /* after split, i1 should be in previous item to i2 */ 2508 ip = i2; 2509 tktadjustind(tkt, TkTbyitemback, &ip); 2510 i1.item = ip.item; 2511 } 2512 2513 lmin = tktprevwrapline(tk, i1.line); 2514 while(i1.item != i2.item) { 2515 if(i1.item->kind != TkTmark) 2516 tktremitem(tkt, &i1); 2517 /* tktremitem moves i1 to next item */ 2518 else { 2519 if(!tktadjustind(tkt, TkTbyitem, &i1)) { 2520 if(tktdbg) 2521 print("tktextdelete botch\n"); 2522 break; 2523 } 2524 } 2525 } 2526 2527 /* 2528 * guard against invalidation of index by tktfixgeom 2529 */ 2530 if (isee.line != nil) 2531 snprint(buf, sizeof(buf), "%d.%d", tktlinenum(tkt, &isee), tktlinepos(tkt, &isee)); 2532 2533 tktfixgeom(tk, lmin, i1.line, 0); 2534 tktextsize(tk, 1); 2535 if(isee.line != nil) { 2536 p = buf; 2537 tktindparse(tk, &p, &isee); 2538 tktsee(tk, &isee, 1); 2539 } 2540 return nil; 2541 } 2542 2543 static char* 2544 tktextsee(Tk *tk, char *arg, char **val) 2545 { 2546 char *e; 2547 TkTindex ix; 2548 2549 USED(val); 2550 2551 e = tktindparse(tk, &arg, &ix); 2552 if(e != nil) 2553 return e; 2554 2555 tktsee(tk, &ix, 0); 2556 return nil; 2557 } 2558 2559 static char* 2560 tktextdelins(Tk *tk, char *arg, char **val) 2561 { 2562 int m, c, skipping, wordc, n; 2563 TkTindex ix, ix2; 2564 TkText *tkt = TKobj(TkText, tk); 2565 char buf[30]; 2566 2567 USED(val); 2568 2569 if(tk->flag&Tkdisabled) 2570 return nil; 2571 2572 if(tktgetsel(tk, &ix, &ix2)) 2573 tktextdelete(tk, "sel.first sel.last", nil); 2574 else { 2575 while(*arg == ' ') 2576 arg++; 2577 if(*arg == '-') { 2578 m = arg[1]; 2579 if(m == 'c') 2580 n = 1; 2581 else { 2582 /* delete prev word (m=='w') or prev line (m=='l') */ 2583 if(!tktmarkind(tk, "insert", &ix)) 2584 return nil; 2585 if(!tktadjustind(tkt, TkTbycharback, &ix)) 2586 return nil; 2587 n = 1; 2588 /* ^W skips back over nonwordchars, then takes maximal seq of wordchars */ 2589 skipping = 1; 2590 for(;;) { 2591 c = tktindrune(&ix); 2592 if(c == '\n') { 2593 /* special case: always delete at least one char */ 2594 if(n > 1) 2595 n--; 2596 break; 2597 } 2598 if(m == 'w') { 2599 wordc = tkiswordchar(c); 2600 if(wordc && skipping) 2601 skipping = 0; 2602 else if(!wordc && !skipping) { 2603 n--; 2604 break; 2605 } 2606 } 2607 if(tktadjustind(tkt, TkTbycharback, &ix)) 2608 n++; 2609 else 2610 break; 2611 } 2612 } 2613 sprint(buf, "insert-%dc insert", n); 2614 tktextdelete(tk, buf, nil); 2615 } 2616 else if(arg[0] == '+' && arg[1] == 'l') 2617 tktextdelete(tk, "insert {insert lineend}", nil); 2618 else 2619 tktextdelete(tk, "insert", nil); 2620 tktextsee(tk, "insert", nil); 2621 } 2622 return nil; 2623 } 2624 2625 static char* 2626 tktextdlineinfo(Tk *tk, char *arg, char **val) 2627 { 2628 char *e; 2629 TkTindex ix; 2630 TkTline *l; 2631 Point p; 2632 int vh; 2633 TkText *tkt = TKobj(TkText, tk); 2634 2635 e = tktindparse(tk, &arg, &ix); 2636 if(e != nil) 2637 return e; 2638 2639 l = ix.line; 2640 vh = tk->act.height; 2641 2642 /* get p in V space */ 2643 p = subpt(l->orig, tkt->deltatv); 2644 if(p.y+l->height < 0 || p.y >= vh) 2645 return nil; 2646 2647 return tkvalue(val, "%d %d %d %d %d", 2648 p.x, p.y, l->width, l->height, l->ascent); 2649 } 2650 2651 static char* 2652 tktextdump(Tk *tk, char *arg, char **val) 2653 { 2654 TkTline *l; 2655 TkTitem *i; 2656 Fmt fmt; 2657 TkText *tkt; 2658 TkDump tkdump; 2659 TkOptab tko[2]; 2660 TkTtaginfo *ti; 2661 TkName *names, *n; 2662 char *e, *win, *p; 2663 TkTindex ix1, ix2; 2664 int r, j, numitems; 2665 ulong fg, bg; 2666 2667 tkt = TKobj(TkText, tk); 2668 2669 2670 tkdump.sgml = 0; 2671 tkdump.metrics = 0; 2672 2673 tko[0].ptr = &tkdump; 2674 tko[0].optab = dumpopts; 2675 tko[1].ptr = nil; 2676 names = nil; 2677 e = tkparse(tk->env->top, arg, tko, &names); 2678 if(e != nil) 2679 return e; 2680 2681 if(names != nil) { /* supplied indices */ 2682 p = names->name; 2683 e = tktindparse(tk, &p, &ix1); 2684 if(e != nil) { 2685 tkfreename(names); 2686 return e; 2687 } 2688 n = names->link; 2689 if(n != nil) { 2690 p = n->name; 2691 e = tktindparse(tk, &p, &ix2); 2692 if(e != nil) { 2693 tkfreename(names); 2694 return e; 2695 } 2696 } 2697 else { 2698 ix2 = ix1; 2699 tktadjustind(tkt, TkTbychar, &ix2); 2700 } 2701 tkfreename(names); 2702 if(!tktindbefore(&ix1, &ix2)) 2703 return nil; 2704 } 2705 else 2706 return TkBadix; 2707 2708 if(tkdump.metrics != 0) { 2709 fmtstrinit(&fmt); 2710 if(fmtprint(&fmt, "%%Fonts\n") < 0) 2711 return TkNomem; 2712 for(ti=tkt->tags; ti != nil; ti=ti->next) { 2713 if(ti->env == nil || ti->env->font == nil) 2714 continue; 2715 if(fmtprint(&fmt, "%d::%s\n", ti->id,ti->env->font->name) < 0) 2716 return TkNomem; 2717 } 2718 if(fmtprint(&fmt, "-1::%s\n%%Colors\n", tk->env->font->name) < 0) 2719 return TkNomem; 2720 for(ti=tkt->tags; ti != nil; ti=ti->next) { 2721 if(ti->env == nil) 2722 continue; 2723 bg = ti->env->colors[TkCbackgnd]; 2724 fg = ti->env->colors[TkCforegnd]; 2725 if(bg == tk->env->colors[TkCbackgnd] && 2726 fg == ti->env->colors[TkCforegnd]) 2727 continue; 2728 r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, bg); 2729 if(r < 0) 2730 return TkNomem; 2731 r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, fg); 2732 if(r < 0) 2733 return TkNomem; 2734 } 2735 if(fmtprint(&fmt, "%%Lines\n") < 0) 2736 return TkNomem; 2737 2738 /* 2739 * In 'metrics' format lines are recorded in the following way: 2740 * xorig yorig wd ht as [data] 2741 * where data is of the form: 2742 * CodeWidth{tags} data 2743 * For Example; 2744 * A200{200000} Hello World! 2745 * denotes an A(scii) contiguous string of 200 pixels with 2746 * bit 20 set in its tags which corresponds to some font. 2747 * 2748 */ 2749 if(ix2.line->items != ix2.item) 2750 ix2.line = ix2.line->next; 2751 for(l = ix1.line; l != ix2.line; l = l->next) { 2752 numitems = 0; 2753 for(i = l->items; i != nil; i = i->next) { 2754 if(i->kind != TkTmark) 2755 numitems++; 2756 } 2757 r = fmtprint(&fmt, "%d %d %d %d %d %d ", 2758 l->orig.x, l->orig.y, l->width, l->height, l->ascent,numitems); 2759 if(r < 0) 2760 return TkNomem; 2761 for(i = l->items; i != nil; i = i->next) { 2762 switch(i->kind) { 2763 case TkTascii: 2764 case TkTrune: 2765 r = i->kind == TkTascii ? 'A' : 'R'; 2766 if(fmtprint(&fmt,"[%c%d{", r, i->width) < 0) 2767 return TkNomem; 2768 if(i->tags !=0 || i->tagextra !=0) { 2769 if(fmtprint(&fmt,"%lux", i->tags[0]) < 0) 2770 return TkNomem; 2771 for(j=0; j < i->tagextra; j++) 2772 if(fmtprint(&fmt,"::%lux", i->tags[j+1]) < 0) 2773 return TkNomem; 2774 } 2775 /* XXX string should be quoted to avoid embedded ']'s */ 2776 if(fmtprint(&fmt,"}%s]", i->istring) < 0) 2777 return TkNomem; 2778 break; 2779 case TkTnewline: 2780 case TkTcontline: 2781 r = i->kind == TkTnewline ? 'N' : 'C'; 2782 if(fmtprint(&fmt, "[%c]", r) < 0) 2783 return TkNomem; 2784 break; 2785 case TkTtab: 2786 if(fmtprint(&fmt,"[T%d]",i->width) < 0) 2787 return TkNomem; 2788 break; 2789 case TkTwin: 2790 win = "<null>"; 2791 if(i->iwin->sub != nil) 2792 win = i->iwin->sub->name->name; 2793 if(fmtprint(&fmt,"[W%d %s]",i->width, win) < 0) 2794 return TkNomem; 2795 break; 2796 } 2797 if(fmtprint(&fmt, " ") < 0) 2798 return TkNomem; 2799 2800 } 2801 if(fmtprint(&fmt, "\n") < 0) 2802 return TkNomem; 2803 *val = fmtstrflush(&fmt); 2804 if(*val == nil) 2805 return TkNomem; 2806 } 2807 } 2808 else 2809 return tktget(tkt, &ix1, &ix2, tkdump.sgml, val); 2810 2811 return nil; 2812 } 2813 2814 2815 static char* 2816 tktextget(Tk *tk, char *arg, char **val) 2817 { 2818 char *e; 2819 TkTindex ix1, ix2; 2820 TkText *tkt = TKobj(TkText, tk); 2821 2822 e = tktindparse(tk, &arg, &ix1); 2823 if(e != nil) 2824 return e; 2825 2826 if(*arg != '\0') { 2827 e = tktindparse(tk, &arg, &ix2); 2828 if(e != nil) 2829 return e; 2830 } 2831 else { 2832 ix2 = ix1; 2833 tktadjustind(tkt, TkTbychar, &ix2); 2834 } 2835 return tktget(tkt, &ix1, &ix2, 0, val); 2836 } 2837 2838 static char* 2839 tktextindex(Tk *tk, char *arg, char **val) 2840 { 2841 char *e; 2842 TkTindex ix; 2843 TkText *tkt = TKobj(TkText, tk); 2844 2845 e = tktindparse(tk, &arg, &ix); 2846 if(e != nil) 2847 return e; 2848 return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix), tktlinepos(tkt, &ix)); 2849 } 2850 2851 static char* 2852 tktextinsert(Tk *tk, char *arg, char **val) 2853 { 2854 int n; 2855 char *e, *p, *pe; 2856 TkTindex ins, pins; 2857 TkTtaginfo *ti; 2858 TkText *tkt; 2859 TkTline *lmin; 2860 TkTop *top; 2861 TkTitem *tagit; 2862 char *tbuf, *buf; 2863 2864 USED(val); 2865 2866 tkt = TKobj(TkText, tk); 2867 top = tk->env->top; 2868 2869 e = tktindparse(tk, &arg, &ins); 2870 if(e != nil) 2871 return e; 2872 2873 if(ins.item->kind == TkTmark) { 2874 if(ins.item->imark->gravity == Tkleft) { 2875 while(ins.item->kind == TkTmark && ins.item->imark->gravity == Tkleft) 2876 if(!tktadjustind(tkt, TkTbyitem, &ins)) { 2877 if(tktdbg) 2878 print("tktextinsert botch\n"); 2879 break; 2880 } 2881 } 2882 else { 2883 for(;;) { 2884 pins = ins; 2885 if(!tktadjustind(tkt, TkTbyitemback, &pins)) 2886 break; 2887 if(pins.item->kind == TkTmark && pins.item->imark->gravity == Tkright) 2888 ins = pins; 2889 else 2890 break; 2891 } 2892 } 2893 } 2894 2895 lmin = tktprevwrapline(tk, ins.line); 2896 2897 n = strlen(arg) + 1; 2898 if(n < Tkmaxitem) 2899 n = Tkmaxitem; 2900 tbuf = malloc(n); 2901 if(tbuf == nil) 2902 return TkNomem; 2903 buf = mallocz(Tkmaxitem, 0); 2904 if(buf == nil) { 2905 free(tbuf); 2906 return TkNomem; 2907 } 2908 2909 tagit = nil; 2910 2911 while(*arg != '\0') { 2912 arg = tkword(top, arg, tbuf, tbuf+n, nil); 2913 if(*arg != '\0') { 2914 /* tag list spec -- add some slop to tagextra for added tags */ 2915 e = tktnewitem(TkTascii, (tkt->nexttag-1)/32 + 1, &tagit); 2916 if(e != nil) { 2917 free(tbuf); 2918 free(buf); 2919 return e; 2920 } 2921 arg = tkword(top, arg, buf, buf+Tkmaxitem, nil); 2922 p = buf; 2923 while(*p) { 2924 while(*p == ' ') { 2925 p++; 2926 } 2927 if(*p == '\0') 2928 break; 2929 pe = strchr(p, ' '); 2930 if(pe != nil) 2931 *pe = '\0'; 2932 ti = tktfindtag(tkt->tags, p); 2933 if(ti == nil) { 2934 e = tktaddtaginfo(tk, p, &ti); 2935 if(e != nil) { 2936 if(tagit != nil) 2937 free(tagit); 2938 free(tbuf); 2939 free(buf); 2940 return e; 2941 } 2942 } 2943 tkttagbit(tagit, ti->id, 1); 2944 if(pe == nil) 2945 break; 2946 else 2947 p = pe+1; 2948 } 2949 } 2950 e = tktinsert(tk, &ins, tbuf, tagit); 2951 if(tagit != nil) { 2952 free(tagit); 2953 tagit = nil; 2954 } 2955 if(e != nil) { 2956 free(tbuf); 2957 free(buf); 2958 return e; 2959 } 2960 } 2961 2962 tktfixgeom(tk, lmin, ins.line, 0); 2963 tktextsize(tk, 1); 2964 2965 free(tbuf); 2966 free(buf); 2967 2968 return nil; 2969 } 2970 2971 static char* 2972 tktextinserti(Tk *tk, char *arg, char **val) 2973 { 2974 int n; 2975 TkTline *lmin; 2976 TkTindex ix, is1, is2; 2977 TkText *tkt = TKobj(TkText, tk); 2978 char *tbuf, *buf; 2979 2980 USED(val); 2981 2982 if(tk->flag&Tkdisabled) 2983 return nil; 2984 2985 buf = mallocz(Tkmaxitem, 0); 2986 if(buf == nil) 2987 return TkNomem; 2988 2989 tbuf = nil; 2990 n = strlen(arg) + 1; 2991 if(n < Tkmaxitem) 2992 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 2993 else { 2994 tbuf = malloc(n); 2995 if(tbuf == nil) { 2996 free(buf); 2997 return TkNomem; 2998 } 2999 tkword(tk->env->top, arg, tbuf, buf+n, nil); 3000 } 3001 if(*buf == '\0') 3002 goto Ret; 3003 if(!tktmarkind(tk, "insert", &ix)) { 3004 print("tktextinserti: botch\n"); 3005 goto Ret; 3006 } 3007 if(tktgetsel(tk, &is1, &is2)) { 3008 if(tktindcompare(tkt, &is1, TkLte, &ix) && 3009 tktindcompare(tkt, &is2, TkGte, &ix)) { 3010 tktextdelete(tk, "sel.first sel.last", nil); 3011 /* delete might have changed ix item */ 3012 tktmarkind(tk, "insert", &ix); 3013 } 3014 } 3015 3016 lmin = tktprevwrapline(tk, ix.line); 3017 tktinsert(tk, &ix, tbuf==nil ? buf : tbuf, 0); 3018 tktfixgeom(tk, lmin, ix.line, 0); 3019 if(tktmarkind(tk, "insert", &ix)) /* index doesn't remain valid after fixgeom */ 3020 tktsee(tk, &ix, 0); 3021 tktextsize(tk, 1); 3022 Ret: 3023 if(tbuf != nil) 3024 free(tbuf); 3025 free(buf); 3026 return nil; 3027 } 3028 3029 static char* 3030 tktextmark(Tk *tk, char *arg, char **val) 3031 { 3032 char *buf; 3033 TkCmdtab *cmd; 3034 3035 buf = mallocz(Tkmaxitem, 0); 3036 if(buf == nil) 3037 return TkNomem; 3038 arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); 3039 for(cmd = tktmarkcmd; cmd->name != nil; cmd++) { 3040 if(strcmp(cmd->name, buf) == 0) { 3041 free(buf); 3042 return cmd->fn(tk, arg, val); 3043 } 3044 } 3045 free(buf); 3046 return TkBadcm; 3047 } 3048 3049 static char* 3050 tktextscan(Tk *tk, char *arg, char **val) 3051 { 3052 char *e; 3053 int mark, x, y, xmax, ymax, vh, vw; 3054 Point p, odeltatv; 3055 char buf[Tkmaxitem]; 3056 TkText *tkt = TKobj(TkText, tk); 3057 3058 USED(val); 3059 3060 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3061 3062 if(strcmp(buf, "mark") == 0) 3063 mark = 1; 3064 else 3065 if(strcmp(buf, "dragto") == 0) 3066 mark = 0; 3067 else 3068 return TkBadcm; 3069 3070 e = tkxyparse(tk, &arg, &p); 3071 if(e != nil) 3072 return e; 3073 3074 if(mark) 3075 tkt->track = p; 3076 else { 3077 odeltatv = tkt->deltatv; 3078 vw = tk->act.width - tk->ipad.x; 3079 vh = tk->act.height - tk->ipad.y; 3080 ymax = tkt->end.prev->orig.y + tkt->end.prev->height - vh; 3081 y = tkt->deltatv.y -10*(p.y - tkt->track.y); 3082 if(y > ymax) 3083 y = ymax; 3084 if(y < 0) 3085 y = 0; 3086 tkt->deltatv.y = y; 3087 e = tktsetscroll(tk, Tkvertical); 3088 if(e != nil) 3089 return e; 3090 if(tkt->opts[TkTwrap] == Tkwrapnone) { 3091 xmax = tktmaxwid(tkt->start.next) - vw; 3092 x = tkt->deltatv.x - 10*(p.x - tkt->track.x); 3093 if(x > xmax) 3094 x = xmax; 3095 if(x < 0) 3096 x = 0; 3097 tkt->deltatv.x = x; 3098 e = tktsetscroll(tk, Tkhorizontal); 3099 if(e != nil) 3100 return e; 3101 } 3102 tktfixscroll(tk, odeltatv); 3103 tkt->track = p; 3104 } 3105 3106 return nil; 3107 } 3108 3109 static char* 3110 tktextscrollpages(Tk *tk, char *arg, char **val) 3111 { 3112 TkText *tkt = TKobj(TkText, tk); 3113 3114 USED(tkt); 3115 USED(arg); 3116 USED(val); 3117 return nil; 3118 } 3119 3120 static char* 3121 tktextsearch(Tk *tk, char *arg, char **val) 3122 { 3123 int i, n; 3124 Rune r; 3125 char *e, *s; 3126 int wrap, fwd, nocase; 3127 TkText *tkt; 3128 TkTindex ix1, ix2, ixstart, ixend, tx; 3129 char buf[Tkmaxitem]; 3130 3131 tkt = TKobj(TkText, tk); 3132 3133 fwd = 1; 3134 nocase = 0; 3135 3136 while(*arg != '\0') { 3137 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3138 if(*buf != '-') 3139 break; 3140 if(strcmp(buf, "-backwards") == 0) 3141 fwd = 0; 3142 else if(strcmp(buf, "-nocase") == 0) 3143 nocase = 1; 3144 else if(strcmp(buf, "--") == 0) { 3145 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3146 break; 3147 } 3148 } 3149 3150 tktstartind(tkt, &ixstart); 3151 tktadjustind(tkt, TkTbycharstart, &ixstart); 3152 tktendind(tkt, &ixend); 3153 3154 if(*arg == '\0') 3155 return TkOparg; 3156 3157 e = tktindparse(tk, &arg, &ix1); 3158 if(e != nil) 3159 return e; 3160 tktadjustind(tkt, fwd? TkTbycharstart : TkTbycharback, &ix1); 3161 3162 if(*arg != '\0') { 3163 wrap = 0; 3164 e = tktindparse(tk, &arg, &ix2); 3165 if(e != nil) 3166 return e; 3167 if(!fwd) 3168 tktadjustind(tkt, TkTbycharback, &ix2); 3169 } 3170 else { 3171 wrap = 1; 3172 if(fwd) { 3173 if(tktindcompare(tkt, &ix1, TkEq, &ixstart)) 3174 ix2 = ixend; 3175 else { 3176 ix2 = ix1; 3177 tktadjustind(tkt, TkTbycharback, &ix2); 3178 } 3179 } 3180 else { 3181 if(tktindcompare(tkt, &ix1, TkEq, &ixend)) 3182 ix2 = ixstart; 3183 else { 3184 ix2 = ix1; 3185 tktadjustind(tkt, TkTbychar, &ix2); 3186 } 3187 } 3188 } 3189 tktadjustind(tkt, TkTbycharstart, &ix2); 3190 if(tktindcompare(tkt, &ix1, TkEq, &ix2)) 3191 return nil; 3192 3193 if(*buf == '\0') 3194 return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1)); 3195 3196 while(!(ix1.item == ix2.item && ix1.pos == ix2.pos)) { 3197 tx = ix1; 3198 for(i = 0; buf[i] != '\0'; i++) { 3199 switch(tx.item->kind) { 3200 case TkTascii: 3201 if(!tktcmatch(tx.item->istring[tx.pos], buf[i], nocase)) 3202 goto nomatch; 3203 break; 3204 case TkTrune: 3205 s = tx.item->istring; 3206 s += tktutfpos(s, tx.pos); 3207 n = chartorune(&r, s); 3208 if(strncmp(s, buf+i, n) != 0) 3209 goto nomatch; 3210 i += n-1; 3211 break; 3212 case TkTtab: 3213 if(buf[i] != '\t') 3214 goto nomatch; 3215 break; 3216 case TkTnewline: 3217 if(buf[i] != '\n') 3218 goto nomatch; 3219 break; 3220 default: 3221 goto nomatch; 3222 } 3223 tktadjustind(tkt, TkTbychar, &tx); 3224 } 3225 return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1)); 3226 nomatch: 3227 if(fwd) { 3228 if(!tktadjustind(tkt, TkTbychar, &ix1)) { 3229 if(!wrap) 3230 break; 3231 ix1 = ixstart; 3232 } 3233 } 3234 else { 3235 if(!tktadjustind(tkt, TkTbycharback, &ix1)) { 3236 if(!wrap) 3237 break; 3238 ix1 = ixend; 3239 } 3240 } 3241 } 3242 3243 return nil; 3244 } 3245 3246 char* 3247 tktextselection(Tk *tk, char *arg, char **val) 3248 { 3249 USED(val); 3250 if (strcmp(arg, " clear") == 0) { 3251 tktclearsel(tk); 3252 return nil; 3253 } 3254 else 3255 return TkBadcm; 3256 } 3257 3258 static void 3259 doselectto(Tk *tk, Point p, int dbl) 3260 { 3261 int halfway; 3262 TkTindex cur, insert, first, last; 3263 TkText *tkt = TKobj(TkText, tk); 3264 tktclearsel(tk); 3265 3266 halfway = tktxyind(tk, p.x, p.y, &cur); 3267 3268 if(!dbl) { 3269 if(!tktmarkind(tk, "insert", &insert)) 3270 insert = cur; 3271 3272 if(tktindcompare(tkt, &cur, TkLt, &insert)) { 3273 first = cur; 3274 last = insert; 3275 } 3276 else { 3277 first = insert; 3278 last = cur; 3279 if(halfway) 3280 tktadjustind(tkt, TkTbychar, &last); 3281 if(last.line == &tkt->end) 3282 tktadjustind(tkt, TkTbycharback, &last); 3283 if(tktindcompare(tkt, &first, TkGte, &last)) 3284 return; 3285 cur = last; 3286 } 3287 tktsee(tk, &cur, 0); 3288 } 3289 else { 3290 first = cur; 3291 last = cur; 3292 tktdoubleclick(tkt, &first, &last); 3293 } 3294 3295 tkttagchange(tk, TkTselid, &first, &last, 1); 3296 } 3297 3298 static void 3299 autoselect(Tk *tk, void *v, int cancelled) 3300 { 3301 TkText *tkt = TKobj(TkText, tk); 3302 Rectangle hitr; 3303 Point p; 3304 USED(v); 3305 3306 if (cancelled) 3307 return; 3308 3309 p = scr2local(tk, tkt->track); 3310 if (tkvisiblerect(tk, &hitr) && ptinrect(p, hitr)) 3311 return; 3312 doselectto(tk, p, 0); 3313 tkdirty(tk); 3314 tkupdate(tk->env->top); 3315 } 3316 3317 static char* 3318 tktextselectto(Tk *tk, char *arg, char **val) 3319 { 3320 int dbl; 3321 char *e; 3322 Point p; 3323 Rectangle hitr; 3324 TkText *tkt = TKobj(TkText, tk); 3325 3326 USED(val); 3327 3328 if(tkt->tflag & (TkTjustfoc|TkTnodrag)) 3329 return nil; 3330 3331 e = tkxyparse(tk, &arg, &p); 3332 if(e != nil) 3333 return e; 3334 tkt->track = p; 3335 p = scr2local(tk, p); 3336 3337 arg = tkskip(arg, " "); 3338 if(*arg == 'd') { 3339 tkcancelrepeat(tk); 3340 dbl = 1; 3341 tkt->tflag |= TkTnodrag; 3342 } else { 3343 dbl = 0; 3344 if (!tkvisiblerect(tk, &hitr) || !ptinrect(p, hitr)) 3345 return nil; 3346 } 3347 doselectto(tk, p, dbl); 3348 return nil; 3349 } 3350 3351 static char tktleft1[] = "{[(<"; 3352 static char tktright1[] = "}])>"; 3353 static char tktleft2[] = "\n"; 3354 static char tktleft3[] = "\'\"`"; 3355 3356 static char *tktleft[] = {tktleft1, tktleft2, tktleft3, nil}; 3357 static char *tktright[] = {tktright1, tktleft2, tktleft3, nil}; 3358 3359 static void 3360 tktdoubleclick(TkText *tkt, TkTindex *first, TkTindex *last) 3361 { 3362 int c, i; 3363 TkTindex ix, ix2; 3364 char *r, *l, *p; 3365 3366 for(i = 0; tktleft[i] != nil; i++) { 3367 ix = *first; 3368 l = tktleft[i]; 3369 r = tktright[i]; 3370 /* try matching character to left, looking right */ 3371 ix2 = ix; 3372 if(!tktadjustind(tkt, TkTbycharback, &ix2)) 3373 c = '\n'; 3374 else 3375 c = tktindrune(&ix2); 3376 p = strchr(l, c); 3377 if(p != nil) { 3378 if(tktclickmatch(tkt, c, r[p-l], 1, &ix)) { 3379 *last = ix; 3380 if(c != '\n') 3381 tktadjustind(tkt, TkTbycharback, last); 3382 } 3383 return; 3384 } 3385 /* try matching character to right, looking left */ 3386 c = tktindrune(&ix); 3387 p = strchr(r, c); 3388 if(p != nil) { 3389 if(tktclickmatch(tkt, c, l[p-r], -1, &ix)) { 3390 *last = *first; 3391 if(c == '\n') 3392 tktadjustind(tkt, TkTbychar, last); 3393 *first = ix; 3394 if(!(c=='\n' && ix.line == tkt->start.next && ix.item == ix.line->items)) 3395 tktadjustind(tkt, TkTbychar, first); 3396 } 3397 return; 3398 } 3399 } 3400 /* try filling out word to right */ 3401 while(tkiswordchar(tktindrune(last))) { 3402 if(!tktadjustind(tkt, TkTbychar, last)) 3403 break; 3404 } 3405 /* try filling out word to left */ 3406 for(;;) { 3407 ix = *first; 3408 if(!tktadjustind(tkt, TkTbycharback, &ix)) 3409 break; 3410 if(!tkiswordchar(tktindrune(&ix))) 3411 break; 3412 *first = ix; 3413 } 3414 } 3415 3416 static int 3417 tktclickmatch(TkText *tkt, int cl, int cr, int dir, TkTindex *ix) 3418 { 3419 int c, nest, atend; 3420 3421 nest = 1; 3422 atend = 0; 3423 for(;;) { 3424 if(dir > 0) { 3425 if(atend) 3426 break; 3427 c = tktindrune(ix); 3428 atend = !tktadjustind(tkt, TkTbychar, ix); 3429 } else { 3430 if(!tktadjustind(tkt, TkTbycharback, ix)) 3431 break; 3432 c = tktindrune(ix); 3433 } 3434 if(c == cr){ 3435 if(--nest==0) 3436 return 1; 3437 }else if(c == cl) 3438 nest++; 3439 } 3440 return cl=='\n' && nest==1; 3441 } 3442 3443 /* 3444 * return the line before line l, unless word wrap is on, 3445 * (for the first word of line l), in which case return the last non-empty line before that. 3446 * tktgeom might then combine the end of that line with the start of the insertion 3447 * (unless there is a newline in the way). 3448 */ 3449 TkTline* 3450 tktprevwrapline(Tk *tk, TkTline *l) 3451 { 3452 TkTitem *i; 3453 int *opts, wrapmode; 3454 TkText *tkt = TKobj(TkText, tk); 3455 TkEnv env; 3456 3457 if(l == nil) 3458 return nil; 3459 /* some spacing depends on tags of first non-mark on display line */ 3460 for(i = l->items; i != nil; i = i->next) 3461 if(i->kind != TkTmark && i->kind != TkTcontline) 3462 break; 3463 if(i == nil || i->kind == TkTnewline) /* can't use !tkanytags(i) because it doesn't check env */ 3464 return l->prev; 3465 opts = mallocz(TkTnumopts*sizeof(int), 0); 3466 if(opts == nil) 3467 return l->prev; /* in worst case gets word wrap wrong */ 3468 tkttagopts(tk, i, opts, &env, nil, 1); 3469 wrapmode = opts[TkTwrap]; 3470 free(opts); 3471 if(wrapmode != Tkwrapword) 3472 return l->prev; 3473 if(l->prev != &tkt->start) 3474 l = l->prev; /* having been processed by tktgeom, shouldn't have extraneous marks etc */ 3475 return l->prev; 3476 } 3477 3478 static char* 3479 tktextsetcursor(Tk *tk, char *arg, char **val) 3480 { 3481 char *e; 3482 TkTindex ix; 3483 TkTmarkinfo *mi; 3484 TkText *tkt = TKobj(TkText, tk); 3485 3486 USED(val); 3487 3488 /* do clearsel here, because it can change indices */ 3489 tktclearsel(tk); 3490 3491 e = tktindparse(tk, &arg, &ix); 3492 if(e != nil) 3493 return e; 3494 3495 mi = tktfindmark(tkt->marks, "insert"); 3496 if(tktdbg && mi == nil) { 3497 print("tktextsetcursor: botch\n"); 3498 return nil; 3499 } 3500 tktmarkmove(tk, mi, &ix); 3501 tktsee(tk, &ix, 0); 3502 return nil; 3503 } 3504 3505 static char* 3506 tktexttag(Tk *tk, char *arg, char **val) 3507 { 3508 char *buf; 3509 TkCmdtab *cmd; 3510 3511 buf = mallocz(Tkmaxitem, 0); 3512 if(buf == nil) 3513 return TkNomem; 3514 arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); 3515 for(cmd = tkttagcmd; cmd->name != nil; cmd++) { 3516 if(strcmp(cmd->name, buf) == 0) { 3517 free(buf); 3518 return cmd->fn(tk, arg, val); 3519 } 3520 } 3521 free(buf); 3522 return TkBadcm; 3523 } 3524 3525 static char* 3526 tktextwindow(Tk *tk, char *arg, char **val) 3527 { 3528 char buf[Tkmaxitem]; 3529 TkCmdtab *cmd; 3530 3531 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3532 for(cmd = tktwincmd; cmd->name != nil; cmd++) { 3533 if(strcmp(cmd->name, buf) == 0) 3534 return cmd->fn(tk, arg, val); 3535 } 3536 return TkBadcm; 3537 } 3538 3539 static char* 3540 tktextxview(Tk *tk, char *arg, char **val) 3541 { 3542 int ntot, vw; 3543 char *e; 3544 Point odeltatv; 3545 TkText *tkt = TKobj(TkText, tk); 3546 3547 odeltatv = tkt->deltatv; 3548 vw = tk->act.width - tk->ipad.x; 3549 ntot = tktmaxwid(tkt->start.next); 3550 if(ntot < tkt->deltatv.x +vw) 3551 ntot = tkt->deltatv.x + vw; 3552 e = tktview(tk, arg, val, vw, &tkt->deltatv.x, ntot, Tkhorizontal); 3553 if(e == nil) { 3554 e = tktsetscroll(tk, Tkhorizontal); 3555 if(e == nil) 3556 tktfixscroll(tk, odeltatv); 3557 } 3558 return e; 3559 } 3560 3561 static int 3562 istext(TkTline *l) 3563 { 3564 TkTitem *i; 3565 3566 for(i = l->items; i != nil; i = i->next) 3567 if(i->kind == TkTwin || i->kind == TkTmark) 3568 return 0; 3569 return 1; 3570 } 3571 3572 static void 3573 tkadjpage(Tk *tk, int ody, int *dy) 3574 { 3575 int y, a, b, d; 3576 TkTindex ix; 3577 TkTline *l; 3578 3579 d = *dy-ody; 3580 y = d > 0 ? tk->act.height : 0; 3581 tktxyind(tk, 0, y-d, &ix); 3582 if((l = ix.line) != nil && istext(l)){ 3583 a = l->orig.y; 3584 b = a+l->height; 3585 /* print("AP: %d %d %d (%d+%d)\n", a, ody+y, b, ody, y); */ 3586 if(a+2 < ody+y && ody+y < b-2){ /* partially obscured line */ 3587 if(d > 0) 3588 *dy -= ody+y-a; 3589 else 3590 *dy += b-ody; 3591 } 3592 } 3593 } 3594 3595 static char* 3596 tktextyview(Tk *tk, char *arg, char **val) 3597 { 3598 int ntot, vh, d; 3599 char *e; 3600 TkTline *l; 3601 Point odeltatv; 3602 TkTindex ix; 3603 TkText *tkt = TKobj(TkText, tk); 3604 char buf[Tkmaxitem], *v; 3605 3606 if(*arg != '\0') { 3607 v = tkitem(buf, arg); 3608 if(strcmp(buf, "-pickplace") == 0) 3609 return tktextsee(tk,v, val); 3610 if(strcmp(buf, "moveto") != 0 && strcmp(buf, "scroll") != 0) { 3611 e = tktindparse(tk, &arg, &ix); 3612 if(e != nil) 3613 return e; 3614 tktsee(tk, &ix, 1); 3615 return nil; 3616 } 3617 } 3618 odeltatv = tkt->deltatv; 3619 vh = tk->act.height; 3620 l = tkt->end.prev; 3621 ntot = l->orig.y + l->height; 3622 // if(ntot < tkt->deltatv.y + vh) 3623 // ntot = tkt->deltatv.y + vh; 3624 e = tktview(tk, arg, val, vh, &tkt->deltatv.y, ntot, Tkvertical); 3625 d = tkt->deltatv.y-odeltatv.y; 3626 if(d == vh || d == -vh) 3627 tkadjpage(tk, odeltatv.y, &tkt->deltatv.y); 3628 if(e == nil) { 3629 e = tktsetscroll(tk, Tkvertical); 3630 if(e == nil) 3631 tktfixscroll(tk, odeltatv); 3632 } 3633 return e; 3634 } 3635 static void 3636 tktextfocusorder(Tk *tk) 3637 { 3638 TkTindex ix; 3639 TkText *t; 3640 Tk *isub; 3641 3642 t = TKobj(TkText, tk); 3643 tktstartind(t, &ix); 3644 do { 3645 if(ix.item->kind == TkTwin) { 3646 isub = ix.item->iwin->sub; 3647 if(isub != nil) 3648 tkappendfocusorder(isub); 3649 } 3650 } while(tktadjustind(t, TkTbyitem, &ix)); 3651 } 3652 3653 TkCmdtab tktextcmd[] = 3654 { 3655 "bbox", tktextbbox, 3656 "cget", tktextcget, 3657 "compare", tktextcompare, 3658 "configure", tktextconfigure, 3659 "debug", tktextdebug, 3660 "delete", tktextdelete, 3661 "dlineinfo", tktextdlineinfo, 3662 "dump", tktextdump, 3663 "get", tktextget, 3664 "index", tktextindex, 3665 "insert", tktextinsert, 3666 "mark", tktextmark, 3667 "scan", tktextscan, 3668 "search", tktextsearch, 3669 "see", tktextsee, 3670 "selection", tktextselection, 3671 "tag", tktexttag, 3672 "window", tktextwindow, 3673 "xview", tktextxview, 3674 "yview", tktextyview, 3675 "tkTextButton1", tktextbutton1, 3676 "tkTextButton1R", tktextbutton1r, 3677 "tkTextDelIns", tktextdelins, 3678 "tkTextInsert", tktextinserti, 3679 "tkTextSelectTo", tktextselectto, 3680 "tkTextSetCursor", tktextsetcursor, 3681 "tkTextScrollPages", tktextscrollpages, 3682 "tkTextCursor", tktextcursor, 3683 nil 3684 }; 3685 3686 TkMethod textmethod = { 3687 "text", 3688 tktextcmd, 3689 tkfreetext, 3690 tkdrawtext, 3691 tktextgeom, 3692 nil, 3693 tktextfocusorder, 3694 tktdirty, 3695 tktrelpos, 3696 tktextevent, 3697 nil, /* XXX need to implement textsee */ 3698 tktinwindow, 3699 nil, 3700 tktxtforgetsub, 3701 }; 3702