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 delete insert"}, 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 +c"}, 109 {TkKey|CNTL('k'), "%W delete insert {insert lineend}"}, 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 2617 tktextdelete(tk, "insert", nil); 2618 tktextsee(tk, "insert", nil); 2619 } 2620 return nil; 2621 } 2622 2623 static char* 2624 tktextdlineinfo(Tk *tk, char *arg, char **val) 2625 { 2626 char *e; 2627 TkTindex ix; 2628 TkTline *l; 2629 Point p; 2630 int vh; 2631 TkText *tkt = TKobj(TkText, tk); 2632 2633 e = tktindparse(tk, &arg, &ix); 2634 if(e != nil) 2635 return e; 2636 2637 l = ix.line; 2638 vh = tk->act.height; 2639 2640 /* get p in V space */ 2641 p = subpt(l->orig, tkt->deltatv); 2642 if(p.y+l->height < 0 || p.y >= vh) 2643 return nil; 2644 2645 return tkvalue(val, "%d %d %d %d %d", 2646 p.x, p.y, l->width, l->height, l->ascent); 2647 } 2648 2649 static char* 2650 tktextdump(Tk *tk, char *arg, char **val) 2651 { 2652 TkTline *l; 2653 TkTitem *i; 2654 Fmt fmt; 2655 TkText *tkt; 2656 TkDump tkdump; 2657 TkOptab tko[2]; 2658 TkTtaginfo *ti; 2659 TkName *names, *n; 2660 char *e, *win, *p; 2661 TkTindex ix1, ix2; 2662 int r, j, numitems; 2663 ulong fg, bg; 2664 2665 tkt = TKobj(TkText, tk); 2666 2667 2668 tkdump.sgml = 0; 2669 tkdump.metrics = 0; 2670 2671 tko[0].ptr = &tkdump; 2672 tko[0].optab = dumpopts; 2673 tko[1].ptr = nil; 2674 names = nil; 2675 e = tkparse(tk->env->top, arg, tko, &names); 2676 if(e != nil) 2677 return e; 2678 2679 if(names != nil) { /* supplied indices */ 2680 p = names->name; 2681 e = tktindparse(tk, &p, &ix1); 2682 if(e != nil) { 2683 tkfreename(names); 2684 return e; 2685 } 2686 n = names->link; 2687 if(n != nil) { 2688 p = n->name; 2689 e = tktindparse(tk, &p, &ix2); 2690 if(e != nil) { 2691 tkfreename(names); 2692 return e; 2693 } 2694 } 2695 else { 2696 ix2 = ix1; 2697 tktadjustind(tkt, TkTbychar, &ix2); 2698 } 2699 tkfreename(names); 2700 if(!tktindbefore(&ix1, &ix2)) 2701 return nil; 2702 } 2703 else 2704 return TkBadix; 2705 2706 if(tkdump.metrics != 0) { 2707 fmtstrinit(&fmt); 2708 if(fmtprint(&fmt, "%%Fonts\n") < 0) 2709 return TkNomem; 2710 for(ti=tkt->tags; ti != nil; ti=ti->next) { 2711 if(ti->env == nil || ti->env->font == nil) 2712 continue; 2713 if(fmtprint(&fmt, "%d::%s\n", ti->id,ti->env->font->name) < 0) 2714 return TkNomem; 2715 } 2716 if(fmtprint(&fmt, "-1::%s\n%%Colors\n", tk->env->font->name) < 0) 2717 return TkNomem; 2718 for(ti=tkt->tags; ti != nil; ti=ti->next) { 2719 if(ti->env == nil) 2720 continue; 2721 bg = ti->env->colors[TkCbackgnd]; 2722 fg = ti->env->colors[TkCforegnd]; 2723 if(bg == tk->env->colors[TkCbackgnd] && 2724 fg == ti->env->colors[TkCforegnd]) 2725 continue; 2726 r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, bg); 2727 if(r < 0) 2728 return TkNomem; 2729 r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, fg); 2730 if(r < 0) 2731 return TkNomem; 2732 } 2733 if(fmtprint(&fmt, "%%Lines\n") < 0) 2734 return TkNomem; 2735 2736 /* 2737 * In 'metrics' format lines are recorded in the following way: 2738 * xorig yorig wd ht as [data] 2739 * where data is of the form: 2740 * CodeWidth{tags} data 2741 * For Example; 2742 * A200{200000} Hello World! 2743 * denotes an A(scii) contiguous string of 200 pixels with 2744 * bit 20 set in its tags which corresponds to some font. 2745 * 2746 */ 2747 if(ix2.line->items != ix2.item) 2748 ix2.line = ix2.line->next; 2749 for(l = ix1.line; l != ix2.line; l = l->next) { 2750 numitems = 0; 2751 for(i = l->items; i != nil; i = i->next) { 2752 if(i->kind != TkTmark) 2753 numitems++; 2754 } 2755 r = fmtprint(&fmt, "%d %d %d %d %d %d ", 2756 l->orig.x, l->orig.y, l->width, l->height, l->ascent,numitems); 2757 if(r < 0) 2758 return TkNomem; 2759 for(i = l->items; i != nil; i = i->next) { 2760 switch(i->kind) { 2761 case TkTascii: 2762 case TkTrune: 2763 r = i->kind == TkTascii ? 'A' : 'R'; 2764 if(fmtprint(&fmt,"[%c%d{", r, i->width) < 0) 2765 return TkNomem; 2766 if(i->tags !=0 || i->tagextra !=0) { 2767 if(fmtprint(&fmt,"%lux", i->tags[0]) < 0) 2768 return TkNomem; 2769 for(j=0; j < i->tagextra; j++) 2770 if(fmtprint(&fmt,"::%lux", i->tags[j+1]) < 0) 2771 return TkNomem; 2772 } 2773 /* XXX string should be quoted to avoid embedded ']'s */ 2774 if(fmtprint(&fmt,"}%s]", i->istring) < 0) 2775 return TkNomem; 2776 break; 2777 case TkTnewline: 2778 case TkTcontline: 2779 r = i->kind == TkTnewline ? 'N' : 'C'; 2780 if(fmtprint(&fmt, "[%c]", r) < 0) 2781 return TkNomem; 2782 break; 2783 case TkTtab: 2784 if(fmtprint(&fmt,"[T%d]",i->width) < 0) 2785 return TkNomem; 2786 break; 2787 case TkTwin: 2788 win = "<null>"; 2789 if(i->iwin->sub != nil) 2790 win = i->iwin->sub->name->name; 2791 if(fmtprint(&fmt,"[W%d %s]",i->width, win) < 0) 2792 return TkNomem; 2793 break; 2794 } 2795 if(fmtprint(&fmt, " ") < 0) 2796 return TkNomem; 2797 2798 } 2799 if(fmtprint(&fmt, "\n") < 0) 2800 return TkNomem; 2801 *val = fmtstrflush(&fmt); 2802 if(*val == nil) 2803 return TkNomem; 2804 } 2805 } 2806 else 2807 return tktget(tkt, &ix1, &ix2, tkdump.sgml, val); 2808 2809 return nil; 2810 } 2811 2812 2813 static char* 2814 tktextget(Tk *tk, char *arg, char **val) 2815 { 2816 char *e; 2817 TkTindex ix1, ix2; 2818 TkText *tkt = TKobj(TkText, tk); 2819 2820 e = tktindparse(tk, &arg, &ix1); 2821 if(e != nil) 2822 return e; 2823 2824 if(*arg != '\0') { 2825 e = tktindparse(tk, &arg, &ix2); 2826 if(e != nil) 2827 return e; 2828 } 2829 else { 2830 ix2 = ix1; 2831 tktadjustind(tkt, TkTbychar, &ix2); 2832 } 2833 return tktget(tkt, &ix1, &ix2, 0, val); 2834 } 2835 2836 static char* 2837 tktextindex(Tk *tk, char *arg, char **val) 2838 { 2839 char *e; 2840 TkTindex ix; 2841 TkText *tkt = TKobj(TkText, tk); 2842 2843 e = tktindparse(tk, &arg, &ix); 2844 if(e != nil) 2845 return e; 2846 return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix), tktlinepos(tkt, &ix)); 2847 } 2848 2849 static char* 2850 tktextinsert(Tk *tk, char *arg, char **val) 2851 { 2852 int n; 2853 char *e, *p, *pe; 2854 TkTindex ins, pins; 2855 TkTtaginfo *ti; 2856 TkText *tkt; 2857 TkTline *lmin; 2858 TkTop *top; 2859 TkTitem *tagit; 2860 char *tbuf, *buf; 2861 2862 USED(val); 2863 2864 tkt = TKobj(TkText, tk); 2865 top = tk->env->top; 2866 2867 e = tktindparse(tk, &arg, &ins); 2868 if(e != nil) 2869 return e; 2870 2871 if(ins.item->kind == TkTmark) { 2872 if(ins.item->imark->gravity == Tkleft) { 2873 while(ins.item->kind == TkTmark && ins.item->imark->gravity == Tkleft) 2874 if(!tktadjustind(tkt, TkTbyitem, &ins)) { 2875 if(tktdbg) 2876 print("tktextinsert botch\n"); 2877 break; 2878 } 2879 } 2880 else { 2881 for(;;) { 2882 pins = ins; 2883 if(!tktadjustind(tkt, TkTbyitemback, &pins)) 2884 break; 2885 if(pins.item->kind == TkTmark && pins.item->imark->gravity == Tkright) 2886 ins = pins; 2887 else 2888 break; 2889 } 2890 } 2891 } 2892 2893 lmin = tktprevwrapline(tk, ins.line); 2894 2895 n = strlen(arg) + 1; 2896 if(n < Tkmaxitem) 2897 n = Tkmaxitem; 2898 tbuf = malloc(n); 2899 if(tbuf == nil) 2900 return TkNomem; 2901 buf = mallocz(Tkmaxitem, 0); 2902 if(buf == nil) { 2903 free(tbuf); 2904 return TkNomem; 2905 } 2906 2907 tagit = nil; 2908 2909 while(*arg != '\0') { 2910 arg = tkword(top, arg, tbuf, tbuf+n, nil); 2911 if(*arg != '\0') { 2912 /* tag list spec -- add some slop to tagextra for added tags */ 2913 e = tktnewitem(TkTascii, (tkt->nexttag-1)/32 + 1, &tagit); 2914 if(e != nil) { 2915 free(tbuf); 2916 free(buf); 2917 return e; 2918 } 2919 arg = tkword(top, arg, buf, buf+Tkmaxitem, nil); 2920 p = buf; 2921 while(*p) { 2922 while(*p == ' ') { 2923 p++; 2924 } 2925 if(*p == '\0') 2926 break; 2927 pe = strchr(p, ' '); 2928 if(pe != nil) 2929 *pe = '\0'; 2930 ti = tktfindtag(tkt->tags, p); 2931 if(ti == nil) { 2932 e = tktaddtaginfo(tk, p, &ti); 2933 if(e != nil) { 2934 if(tagit != nil) 2935 free(tagit); 2936 free(tbuf); 2937 free(buf); 2938 return e; 2939 } 2940 } 2941 tkttagbit(tagit, ti->id, 1); 2942 if(pe == nil) 2943 break; 2944 else 2945 p = pe+1; 2946 } 2947 } 2948 e = tktinsert(tk, &ins, tbuf, tagit); 2949 if(tagit != nil) { 2950 free(tagit); 2951 tagit = nil; 2952 } 2953 if(e != nil) { 2954 free(tbuf); 2955 free(buf); 2956 return e; 2957 } 2958 } 2959 2960 tktfixgeom(tk, lmin, ins.line, 0); 2961 tktextsize(tk, 1); 2962 2963 free(tbuf); 2964 free(buf); 2965 2966 return nil; 2967 } 2968 2969 static char* 2970 tktextinserti(Tk *tk, char *arg, char **val) 2971 { 2972 int n; 2973 TkTline *lmin; 2974 TkTindex ix, is1, is2; 2975 TkText *tkt = TKobj(TkText, tk); 2976 char *tbuf, *buf; 2977 2978 USED(val); 2979 2980 if(tk->flag&Tkdisabled) 2981 return nil; 2982 2983 buf = mallocz(Tkmaxitem, 0); 2984 if(buf == nil) 2985 return TkNomem; 2986 2987 tbuf = nil; 2988 n = strlen(arg) + 1; 2989 if(n < Tkmaxitem) 2990 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 2991 else { 2992 tbuf = malloc(n); 2993 if(tbuf == nil) { 2994 free(buf); 2995 return TkNomem; 2996 } 2997 tkword(tk->env->top, arg, tbuf, buf+n, nil); 2998 } 2999 if(*buf == '\0') 3000 goto Ret; 3001 if(!tktmarkind(tk, "insert", &ix)) { 3002 print("tktextinserti: botch\n"); 3003 goto Ret; 3004 } 3005 if(tktgetsel(tk, &is1, &is2)) { 3006 if(tktindcompare(tkt, &is1, TkLte, &ix) && 3007 tktindcompare(tkt, &is2, TkGte, &ix)) { 3008 tktextdelete(tk, "sel.first sel.last", nil); 3009 /* delete might have changed ix item */ 3010 tktmarkind(tk, "insert", &ix); 3011 } 3012 } 3013 3014 lmin = tktprevwrapline(tk, ix.line); 3015 tktinsert(tk, &ix, tbuf==nil ? buf : tbuf, 0); 3016 tktfixgeom(tk, lmin, ix.line, 0); 3017 if(tktmarkind(tk, "insert", &ix)) /* index doesn't remain valid after fixgeom */ 3018 tktsee(tk, &ix, 0); 3019 tktextsize(tk, 1); 3020 Ret: 3021 if(tbuf != nil) 3022 free(tbuf); 3023 free(buf); 3024 return nil; 3025 } 3026 3027 static char* 3028 tktextmark(Tk *tk, char *arg, char **val) 3029 { 3030 char *buf; 3031 TkCmdtab *cmd; 3032 3033 buf = mallocz(Tkmaxitem, 0); 3034 if(buf == nil) 3035 return TkNomem; 3036 arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); 3037 for(cmd = tktmarkcmd; cmd->name != nil; cmd++) { 3038 if(strcmp(cmd->name, buf) == 0) { 3039 free(buf); 3040 return cmd->fn(tk, arg, val); 3041 } 3042 } 3043 free(buf); 3044 return TkBadcm; 3045 } 3046 3047 static char* 3048 tktextscan(Tk *tk, char *arg, char **val) 3049 { 3050 char *e; 3051 int mark, x, y, xmax, ymax, vh, vw; 3052 Point p, odeltatv; 3053 char buf[Tkmaxitem]; 3054 TkText *tkt = TKobj(TkText, tk); 3055 3056 USED(val); 3057 3058 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3059 3060 if(strcmp(buf, "mark") == 0) 3061 mark = 1; 3062 else 3063 if(strcmp(buf, "dragto") == 0) 3064 mark = 0; 3065 else 3066 return TkBadcm; 3067 3068 e = tkxyparse(tk, &arg, &p); 3069 if(e != nil) 3070 return e; 3071 3072 if(mark) 3073 tkt->track = p; 3074 else { 3075 odeltatv = tkt->deltatv; 3076 vw = tk->act.width - tk->ipad.x; 3077 vh = tk->act.height - tk->ipad.y; 3078 ymax = tkt->end.prev->orig.y + tkt->end.prev->height - vh; 3079 y = tkt->deltatv.y -10*(p.y - tkt->track.y); 3080 if(y > ymax) 3081 y = ymax; 3082 if(y < 0) 3083 y = 0; 3084 tkt->deltatv.y = y; 3085 e = tktsetscroll(tk, Tkvertical); 3086 if(e != nil) 3087 return e; 3088 if(tkt->opts[TkTwrap] == Tkwrapnone) { 3089 xmax = tktmaxwid(tkt->start.next) - vw; 3090 x = tkt->deltatv.x - 10*(p.x - tkt->track.x); 3091 if(x > xmax) 3092 x = xmax; 3093 if(x < 0) 3094 x = 0; 3095 tkt->deltatv.x = x; 3096 e = tktsetscroll(tk, Tkhorizontal); 3097 if(e != nil) 3098 return e; 3099 } 3100 tktfixscroll(tk, odeltatv); 3101 tkt->track = p; 3102 } 3103 3104 return nil; 3105 } 3106 3107 static char* 3108 tktextscrollpages(Tk *tk, char *arg, char **val) 3109 { 3110 TkText *tkt = TKobj(TkText, tk); 3111 3112 USED(tkt); 3113 USED(arg); 3114 USED(val); 3115 return nil; 3116 } 3117 3118 static char* 3119 tktextsearch(Tk *tk, char *arg, char **val) 3120 { 3121 int i, n; 3122 Rune r; 3123 char *e, *s; 3124 int wrap, fwd, nocase; 3125 TkText *tkt; 3126 TkTindex ix1, ix2, ixstart, ixend, tx; 3127 char buf[Tkmaxitem]; 3128 3129 tkt = TKobj(TkText, tk); 3130 3131 fwd = 1; 3132 nocase = 0; 3133 3134 while(*arg != '\0') { 3135 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3136 if(*buf != '-') 3137 break; 3138 if(strcmp(buf, "-backwards") == 0) 3139 fwd = 0; 3140 else if(strcmp(buf, "-nocase") == 0) 3141 nocase = 1; 3142 else if(strcmp(buf, "--") == 0) { 3143 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3144 break; 3145 } 3146 } 3147 3148 tktstartind(tkt, &ixstart); 3149 tktadjustind(tkt, TkTbycharstart, &ixstart); 3150 tktendind(tkt, &ixend); 3151 3152 if(*arg == '\0') 3153 return TkOparg; 3154 3155 e = tktindparse(tk, &arg, &ix1); 3156 if(e != nil) 3157 return e; 3158 tktadjustind(tkt, fwd? TkTbycharstart : TkTbycharback, &ix1); 3159 3160 if(*arg != '\0') { 3161 wrap = 0; 3162 e = tktindparse(tk, &arg, &ix2); 3163 if(e != nil) 3164 return e; 3165 if(!fwd) 3166 tktadjustind(tkt, TkTbycharback, &ix2); 3167 } 3168 else { 3169 wrap = 1; 3170 if(fwd) { 3171 if(tktindcompare(tkt, &ix1, TkEq, &ixstart)) 3172 ix2 = ixend; 3173 else { 3174 ix2 = ix1; 3175 tktadjustind(tkt, TkTbycharback, &ix2); 3176 } 3177 } 3178 else { 3179 if(tktindcompare(tkt, &ix1, TkEq, &ixend)) 3180 ix2 = ixstart; 3181 else { 3182 ix2 = ix1; 3183 tktadjustind(tkt, TkTbychar, &ix2); 3184 } 3185 } 3186 } 3187 tktadjustind(tkt, TkTbycharstart, &ix2); 3188 if(tktindcompare(tkt, &ix1, TkEq, &ix2)) 3189 return nil; 3190 3191 if(*buf == '\0') 3192 return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1)); 3193 3194 while(!(ix1.item == ix2.item && ix1.pos == ix2.pos)) { 3195 tx = ix1; 3196 for(i = 0; buf[i] != '\0'; i++) { 3197 switch(tx.item->kind) { 3198 case TkTascii: 3199 if(!tktcmatch(tx.item->istring[tx.pos], buf[i], nocase)) 3200 goto nomatch; 3201 break; 3202 case TkTrune: 3203 s = tx.item->istring; 3204 s += tktutfpos(s, tx.pos); 3205 n = chartorune(&r, s); 3206 if(strncmp(s, buf+i, n) != 0) 3207 goto nomatch; 3208 i += n-1; 3209 break; 3210 case TkTtab: 3211 if(buf[i] != '\t') 3212 goto nomatch; 3213 break; 3214 case TkTnewline: 3215 if(buf[i] != '\n') 3216 goto nomatch; 3217 break; 3218 default: 3219 goto nomatch; 3220 } 3221 tktadjustind(tkt, TkTbychar, &tx); 3222 } 3223 return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1)); 3224 nomatch: 3225 if(fwd) { 3226 if(!tktadjustind(tkt, TkTbychar, &ix1)) { 3227 if(!wrap) 3228 break; 3229 ix1 = ixstart; 3230 } 3231 } 3232 else { 3233 if(!tktadjustind(tkt, TkTbycharback, &ix1)) { 3234 if(!wrap) 3235 break; 3236 ix1 = ixend; 3237 } 3238 } 3239 } 3240 3241 return nil; 3242 } 3243 3244 char* 3245 tktextselection(Tk *tk, char *arg, char **val) 3246 { 3247 USED(val); 3248 if (strcmp(arg, " clear") == 0) { 3249 tktclearsel(tk); 3250 return nil; 3251 } 3252 else 3253 return TkBadcm; 3254 } 3255 3256 static void 3257 doselectto(Tk *tk, Point p, int dbl) 3258 { 3259 int halfway; 3260 TkTindex cur, insert, first, last; 3261 TkText *tkt = TKobj(TkText, tk); 3262 tktclearsel(tk); 3263 3264 halfway = tktxyind(tk, p.x, p.y, &cur); 3265 3266 if(!dbl) { 3267 if(!tktmarkind(tk, "insert", &insert)) 3268 insert = cur; 3269 3270 if(tktindcompare(tkt, &cur, TkLt, &insert)) { 3271 first = cur; 3272 last = insert; 3273 } 3274 else { 3275 first = insert; 3276 last = cur; 3277 if(halfway) 3278 tktadjustind(tkt, TkTbychar, &last); 3279 if(last.line == &tkt->end) 3280 tktadjustind(tkt, TkTbycharback, &last); 3281 if(tktindcompare(tkt, &first, TkGte, &last)) 3282 return; 3283 cur = last; 3284 } 3285 tktsee(tk, &cur, 0); 3286 } 3287 else { 3288 first = cur; 3289 last = cur; 3290 tktdoubleclick(tkt, &first, &last); 3291 } 3292 3293 tkttagchange(tk, TkTselid, &first, &last, 1); 3294 } 3295 3296 static void 3297 autoselect(Tk *tk, void *v, int cancelled) 3298 { 3299 TkText *tkt = TKobj(TkText, tk); 3300 Rectangle hitr; 3301 Point p; 3302 USED(v); 3303 3304 if (cancelled) 3305 return; 3306 3307 p = scr2local(tk, tkt->track); 3308 if (tkvisiblerect(tk, &hitr) && ptinrect(p, hitr)) 3309 return; 3310 doselectto(tk, p, 0); 3311 tkdirty(tk); 3312 tkupdate(tk->env->top); 3313 } 3314 3315 static char* 3316 tktextselectto(Tk *tk, char *arg, char **val) 3317 { 3318 int dbl; 3319 char *e; 3320 Point p; 3321 Rectangle hitr; 3322 TkText *tkt = TKobj(TkText, tk); 3323 3324 USED(val); 3325 3326 if(tkt->tflag & (TkTjustfoc|TkTnodrag)) 3327 return nil; 3328 3329 e = tkxyparse(tk, &arg, &p); 3330 if(e != nil) 3331 return e; 3332 tkt->track = p; 3333 p = scr2local(tk, p); 3334 3335 arg = tkskip(arg, " "); 3336 if(*arg == 'd') { 3337 tkcancelrepeat(tk); 3338 dbl = 1; 3339 tkt->tflag |= TkTnodrag; 3340 } else { 3341 dbl = 0; 3342 if (!tkvisiblerect(tk, &hitr) || !ptinrect(p, hitr)) 3343 return nil; 3344 } 3345 doselectto(tk, p, dbl); 3346 return nil; 3347 } 3348 3349 static char tktleft1[] = "{[(<"; 3350 static char tktright1[] = "}])>"; 3351 static char tktleft2[] = "\n"; 3352 static char tktleft3[] = "\'\"`"; 3353 3354 static char *tktleft[] = {tktleft1, tktleft2, tktleft3, nil}; 3355 static char *tktright[] = {tktright1, tktleft2, tktleft3, nil}; 3356 3357 static void 3358 tktdoubleclick(TkText *tkt, TkTindex *first, TkTindex *last) 3359 { 3360 int c, i; 3361 TkTindex ix, ix2; 3362 char *r, *l, *p; 3363 3364 for(i = 0; tktleft[i] != nil; i++) { 3365 ix = *first; 3366 l = tktleft[i]; 3367 r = tktright[i]; 3368 /* try matching character to left, looking right */ 3369 ix2 = ix; 3370 if(!tktadjustind(tkt, TkTbycharback, &ix2)) 3371 c = '\n'; 3372 else 3373 c = tktindrune(&ix2); 3374 p = strchr(l, c); 3375 if(p != nil) { 3376 if(tktclickmatch(tkt, c, r[p-l], 1, &ix)) { 3377 *last = ix; 3378 if(c != '\n') 3379 tktadjustind(tkt, TkTbycharback, last); 3380 } 3381 return; 3382 } 3383 /* try matching character to right, looking left */ 3384 c = tktindrune(&ix); 3385 p = strchr(r, c); 3386 if(p != nil) { 3387 if(tktclickmatch(tkt, c, l[p-r], -1, &ix)) { 3388 *last = *first; 3389 if(c == '\n') 3390 tktadjustind(tkt, TkTbychar, last); 3391 *first = ix; 3392 if(!(c=='\n' && ix.line == tkt->start.next && ix.item == ix.line->items)) 3393 tktadjustind(tkt, TkTbychar, first); 3394 } 3395 return; 3396 } 3397 } 3398 /* try filling out word to right */ 3399 while(tkiswordchar(tktindrune(last))) { 3400 if(!tktadjustind(tkt, TkTbychar, last)) 3401 break; 3402 } 3403 /* try filling out word to left */ 3404 for(;;) { 3405 ix = *first; 3406 if(!tktadjustind(tkt, TkTbycharback, &ix)) 3407 break; 3408 if(!tkiswordchar(tktindrune(&ix))) 3409 break; 3410 *first = ix; 3411 } 3412 } 3413 3414 static int 3415 tktclickmatch(TkText *tkt, int cl, int cr, int dir, TkTindex *ix) 3416 { 3417 int c, nest, atend; 3418 3419 nest = 1; 3420 atend = 0; 3421 for(;;) { 3422 if(dir > 0) { 3423 if(atend) 3424 break; 3425 c = tktindrune(ix); 3426 atend = !tktadjustind(tkt, TkTbychar, ix); 3427 } else { 3428 if(!tktadjustind(tkt, TkTbycharback, ix)) 3429 break; 3430 c = tktindrune(ix); 3431 } 3432 if(c == cr){ 3433 if(--nest==0) 3434 return 1; 3435 }else if(c == cl) 3436 nest++; 3437 } 3438 return cl=='\n' && nest==1; 3439 } 3440 3441 /* 3442 * return the line before line l, unless word wrap is on, 3443 * (for the first word of line l), in which case return the last non-empty line before that. 3444 * tktgeom might then combine the end of that line with the start of the insertion 3445 * (unless there is a newline in the way). 3446 */ 3447 TkTline* 3448 tktprevwrapline(Tk *tk, TkTline *l) 3449 { 3450 TkTitem *i; 3451 int *opts, wrapmode; 3452 TkText *tkt = TKobj(TkText, tk); 3453 TkEnv env; 3454 3455 if(l == nil) 3456 return nil; 3457 /* some spacing depends on tags of first non-mark on display line */ 3458 for(i = l->items; i != nil; i = i->next) 3459 if(i->kind != TkTmark && i->kind != TkTcontline) 3460 break; 3461 if(i == nil || i->kind == TkTnewline) /* can't use !tkanytags(i) because it doesn't check env */ 3462 return l->prev; 3463 opts = mallocz(TkTnumopts*sizeof(int), 0); 3464 if(opts == nil) 3465 return l->prev; /* in worst case gets word wrap wrong */ 3466 tkttagopts(tk, i, opts, &env, nil, 1); 3467 wrapmode = opts[TkTwrap]; 3468 free(opts); 3469 if(wrapmode != Tkwrapword) 3470 return l->prev; 3471 if(l->prev != &tkt->start) 3472 l = l->prev; /* having been processed by tktgeom, shouldn't have extraneous marks etc */ 3473 return l->prev; 3474 } 3475 3476 static char* 3477 tktextsetcursor(Tk *tk, char *arg, char **val) 3478 { 3479 char *e; 3480 TkTindex ix; 3481 TkTmarkinfo *mi; 3482 TkText *tkt = TKobj(TkText, tk); 3483 3484 USED(val); 3485 3486 /* do clearsel here, because it can change indices */ 3487 tktclearsel(tk); 3488 3489 e = tktindparse(tk, &arg, &ix); 3490 if(e != nil) 3491 return e; 3492 3493 mi = tktfindmark(tkt->marks, "insert"); 3494 if(tktdbg && mi == nil) { 3495 print("tktextsetcursor: botch\n"); 3496 return nil; 3497 } 3498 tktmarkmove(tk, mi, &ix); 3499 tktsee(tk, &ix, 0); 3500 return nil; 3501 } 3502 3503 static char* 3504 tktexttag(Tk *tk, char *arg, char **val) 3505 { 3506 char *buf; 3507 TkCmdtab *cmd; 3508 3509 buf = mallocz(Tkmaxitem, 0); 3510 if(buf == nil) 3511 return TkNomem; 3512 arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); 3513 for(cmd = tkttagcmd; cmd->name != nil; cmd++) { 3514 if(strcmp(cmd->name, buf) == 0) { 3515 free(buf); 3516 return cmd->fn(tk, arg, val); 3517 } 3518 } 3519 free(buf); 3520 return TkBadcm; 3521 } 3522 3523 static char* 3524 tktextwindow(Tk *tk, char *arg, char **val) 3525 { 3526 char buf[Tkmaxitem]; 3527 TkCmdtab *cmd; 3528 3529 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3530 for(cmd = tktwincmd; cmd->name != nil; cmd++) { 3531 if(strcmp(cmd->name, buf) == 0) 3532 return cmd->fn(tk, arg, val); 3533 } 3534 return TkBadcm; 3535 } 3536 3537 static char* 3538 tktextxview(Tk *tk, char *arg, char **val) 3539 { 3540 int ntot, vw; 3541 char *e; 3542 Point odeltatv; 3543 TkText *tkt = TKobj(TkText, tk); 3544 3545 odeltatv = tkt->deltatv; 3546 vw = tk->act.width - tk->ipad.x; 3547 ntot = tktmaxwid(tkt->start.next); 3548 if(ntot < tkt->deltatv.x +vw) 3549 ntot = tkt->deltatv.x + vw; 3550 e = tktview(tk, arg, val, vw, &tkt->deltatv.x, ntot, Tkhorizontal); 3551 if(e == nil) { 3552 e = tktsetscroll(tk, Tkhorizontal); 3553 if(e == nil) 3554 tktfixscroll(tk, odeltatv); 3555 } 3556 return e; 3557 } 3558 3559 static int 3560 istext(TkTline *l) 3561 { 3562 TkTitem *i; 3563 3564 for(i = l->items; i != nil; i = i->next) 3565 if(i->kind == TkTwin || i->kind == TkTmark) 3566 return 0; 3567 return 1; 3568 } 3569 3570 static void 3571 tkadjpage(Tk *tk, int ody, int *dy) 3572 { 3573 int y, a, b, d; 3574 TkTindex ix; 3575 TkTline *l; 3576 3577 d = *dy-ody; 3578 y = d > 0 ? tk->act.height : 0; 3579 tktxyind(tk, 0, y-d, &ix); 3580 if((l = ix.line) != nil && istext(l)){ 3581 a = l->orig.y; 3582 b = a+l->height; 3583 /* print("AP: %d %d %d (%d+%d)\n", a, ody+y, b, ody, y); */ 3584 if(a+2 < ody+y && ody+y < b-2){ /* partially obscured line */ 3585 if(d > 0) 3586 *dy -= ody+y-a; 3587 else 3588 *dy += b-ody; 3589 } 3590 } 3591 } 3592 3593 static char* 3594 tktextyview(Tk *tk, char *arg, char **val) 3595 { 3596 int ntot, vh, d; 3597 char *e; 3598 TkTline *l; 3599 Point odeltatv; 3600 TkTindex ix; 3601 TkText *tkt = TKobj(TkText, tk); 3602 char buf[Tkmaxitem], *v; 3603 3604 if(*arg != '\0') { 3605 v = tkitem(buf, arg); 3606 if(strcmp(buf, "-pickplace") == 0) 3607 return tktextsee(tk,v, val); 3608 if(strcmp(buf, "moveto") != 0 && strcmp(buf, "scroll") != 0) { 3609 e = tktindparse(tk, &arg, &ix); 3610 if(e != nil) 3611 return e; 3612 tktsee(tk, &ix, 1); 3613 return nil; 3614 } 3615 } 3616 odeltatv = tkt->deltatv; 3617 vh = tk->act.height; 3618 l = tkt->end.prev; 3619 ntot = l->orig.y + l->height; 3620 // if(ntot < tkt->deltatv.y + vh) 3621 // ntot = tkt->deltatv.y + vh; 3622 e = tktview(tk, arg, val, vh, &tkt->deltatv.y, ntot, Tkvertical); 3623 d = tkt->deltatv.y-odeltatv.y; 3624 if(d == vh || d == -vh) 3625 tkadjpage(tk, odeltatv.y, &tkt->deltatv.y); 3626 if(e == nil) { 3627 e = tktsetscroll(tk, Tkvertical); 3628 if(e == nil) 3629 tktfixscroll(tk, odeltatv); 3630 } 3631 return e; 3632 } 3633 static void 3634 tktextfocusorder(Tk *tk) 3635 { 3636 TkTindex ix; 3637 TkText *t; 3638 Tk *isub; 3639 3640 t = TKobj(TkText, tk); 3641 tktstartind(t, &ix); 3642 do { 3643 if(ix.item->kind == TkTwin) { 3644 isub = ix.item->iwin->sub; 3645 if(isub != nil) 3646 tkappendfocusorder(isub); 3647 } 3648 } while(tktadjustind(t, TkTbyitem, &ix)); 3649 } 3650 3651 TkCmdtab tktextcmd[] = 3652 { 3653 "bbox", tktextbbox, 3654 "cget", tktextcget, 3655 "compare", tktextcompare, 3656 "configure", tktextconfigure, 3657 "debug", tktextdebug, 3658 "delete", tktextdelete, 3659 "dlineinfo", tktextdlineinfo, 3660 "dump", tktextdump, 3661 "get", tktextget, 3662 "index", tktextindex, 3663 "insert", tktextinsert, 3664 "mark", tktextmark, 3665 "scan", tktextscan, 3666 "search", tktextsearch, 3667 "see", tktextsee, 3668 "selection", tktextselection, 3669 "tag", tktexttag, 3670 "window", tktextwindow, 3671 "xview", tktextxview, 3672 "yview", tktextyview, 3673 "tkTextButton1", tktextbutton1, 3674 "tkTextButton1R", tktextbutton1r, 3675 "tkTextDelIns", tktextdelins, 3676 "tkTextInsert", tktextinserti, 3677 "tkTextSelectTo", tktextselectto, 3678 "tkTextSetCursor", tktextsetcursor, 3679 "tkTextScrollPages", tktextscrollpages, 3680 "tkTextCursor", tktextcursor, 3681 nil 3682 }; 3683 3684 TkMethod textmethod = { 3685 "text", 3686 tktextcmd, 3687 tkfreetext, 3688 tkdrawtext, 3689 tktextgeom, 3690 nil, 3691 tktextfocusorder, 3692 tktdirty, 3693 tktrelpos, 3694 tktextevent, 3695 nil, /* XXX need to implement textsee */ 3696 tktinwindow, 3697 nil, 3698 tktxtforgetsub, 3699 }; 3700