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