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 1 page"}, 117 {TkKey|Pgdown, "%W yview scroll 1 page"}, 118 {TkKey|CNTL('w'), "%W tkTextDelIns -w"}, 119 {TkKey|Pgup, "%W yview scroll -1 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 = 2; 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 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 1803 amount = atoi(buf); 1804 arg = tkskip(arg, " \t"); 1805 if(*arg == 'p') /* Pages */ 1806 amount *= nl; 1807 else /* Lines or Characters */ 1808 if(orient == Tkvertical) { 1809 /* XXX needs improvement */ 1810 amount *= tk->env->font->height; 1811 } 1812 else 1813 amount *= tk->env->wzero; 1814 n = *posn + amount; 1815 if(n < 0) 1816 n = 0; 1817 if(n > max) 1818 n = max; 1819 *posn = n; 1820 } 1821 else 1822 return TkBadcm; 1823 1824 bot = max - (nl * 3 / 4); 1825 if(*posn > bot) 1826 *posn = bot; 1827 if(*posn < 0) 1828 *posn = 0; 1829 1830 return nil; 1831 } 1832 1833 static void 1834 tktclearsel(Tk *tk) 1835 { 1836 TkTindex ibeg, iend; 1837 TkText *tkt = TKobj(TkText, tk); 1838 1839 if(tkt->selfirst == nil) 1840 return; 1841 tktitemind(tkt->selfirst, &ibeg); 1842 tktitemind(tkt->sellast, &iend); 1843 1844 tkttagchange(tk, TkTselid, &ibeg, &iend, 0); 1845 } 1846 1847 static int 1848 tktgetsel(Tk *tk, TkTindex *i1, TkTindex *i2) 1849 { 1850 TkText *tkt =TKobj(TkText, tk); 1851 1852 if(tkt->selfirst == nil) 1853 return 0; 1854 tktitemind(tkt->selfirst, i1); 1855 tktitemind(tkt->sellast, i2); 1856 return 1; 1857 } 1858 1859 /* 1860 * Adjust tkt->deltatv so that indexed character is visible. 1861 * - if seetop is true, make indexed char be at top of window 1862 * - if it is already visible, do nothing. 1863 * - if it is > 1/2 screenful off edge of screen, center it 1864 * else put it at bottom or top (whichever is nearer) 1865 * - if first line is visible, put it at top 1866 * - if last line is visible, allow one blank line at bottom 1867 * 1868 * BUG: should handle x visibility too 1869 */ 1870 static void 1871 tktsee(Tk *tk, TkTindex *ixp, int seetop) 1872 { 1873 int ycur, ynext, deltatvy, adjy, h; 1874 Point p, odeltatv; 1875 Rectangle bbox; 1876 TkTline *l, *el; 1877 TkText *tkt = TKobj(TkText, tk); 1878 TkTindex ix; 1879 1880 ix = *ixp; 1881 deltatvy = tkt->deltatv.y; 1882 odeltatv = tkt->deltatv; 1883 h = tk->act.height; 1884 1885 /* find p (in T space): top left of indexed line */ 1886 l = ix.line; 1887 p = l->orig; 1888 1889 /* ycur, ynext in V space */ 1890 ycur = p.y - deltatvy; 1891 ynext = ycur + l->height; 1892 adjy = 0; 1893 1894 /* quantize h to line boundaries (works if single font) */ 1895 if ( l->height ) 1896 h -= h%l->height; 1897 1898 if(seetop) { 1899 deltatvy = p.y; 1900 adjy = 1; 1901 } 1902 else 1903 if(ycur < 0 || ynext >= h) { 1904 adjy = 1; 1905 1906 if(ycur < -h/2 || ycur > 3*h/2) 1907 deltatvy = p.y - h/2; 1908 else if(ycur < 0) 1909 deltatvy = p.y; 1910 else 1911 deltatvy = p.y - h + l->height; 1912 1913 el = tkt->end.prev; 1914 if(el != nil && el->orig.y - deltatvy < h) 1915 deltatvy = tkt->end.orig.y - (h * 3 / 4); 1916 1917 if(p.y - deltatvy < 0) 1918 deltatvy = p.y; 1919 if(deltatvy < 0) 1920 deltatvy = 0; 1921 } 1922 if(adjy) { 1923 tkt->deltatv.y = deltatvy; 1924 tktsetscroll(tk, Tkvertical); /* XXX - Tad: err ignored */ 1925 tktfixscroll(tk, odeltatv); 1926 } 1927 while (ix.item->kind == TkTmark) 1928 ix.item = ix.item->next; 1929 bbox = tktbbox(tk, &ix); 1930 /* make sure that cursor at the end gets shown */ 1931 tksee(tk, bbox, Pt(bbox.min.x, (bbox.min.y + bbox.max.y) / 2)); 1932 } 1933 1934 static int 1935 tktcmatch(int c1, int c2, int nocase) 1936 { 1937 if(nocase) { 1938 if(c1 >= 'a' && c1 <= 'z') 1939 c1 -= 'a' - 'A'; 1940 if(c2 >= 'a' && c2 <= 'z') 1941 c2 -= 'a' - 'A'; 1942 } 1943 return (c1 == c2); 1944 } 1945 1946 /* 1947 * Return 1 if tag with id m1 ends before tag with id m2, 1948 * starting at the item after that indexed in ix (but don't 1949 * modify ix). 1950 */ 1951 static int 1952 tagendsbefore(TkText *tkt, TkTindex *ix, int m1, int m2) 1953 { 1954 int s1, s2; 1955 TkTindex ix1; 1956 TkTitem *i; 1957 1958 ix1 = *ix; 1959 while(tktadjustind(tkt, TkTbyitem, &ix1)) { 1960 i = ix1.item; 1961 if(i->kind == TkTwin || i->kind == TkTcontline || i->kind == TkTmark) 1962 continue; 1963 s1 = tkttagset(i, m1); 1964 s2 = tkttagset(i, m2); 1965 if(!s1) 1966 return s2; 1967 else if(!s2) 1968 return 0; 1969 } 1970 return 0; 1971 } 1972 1973 static int 1974 tktsgmltags(TkText *tkt, Fmt *fmt, TkTitem *iprev, TkTitem *i, TkTindex *ix, int *stack, int *pnstack, int *tmpstack) 1975 { 1976 int nprev, n, m, r, k, j, ii, onstack, nt; 1977 1978 nprev = 0; 1979 if(iprev != nil && (iprev->tags[0] != 0 || iprev->tagextra > 0)) 1980 nprev = 32*(iprev->tagextra + 1); 1981 n = 0; 1982 if(i != nil && (i->tags[0] != 0 || i->tagextra > 0)) 1983 n = 32*(i->tagextra + 1); 1984 nt = 0; 1985 if(n > 0) { 1986 /* find tags which open here */ 1987 for(m = 0; m < n; m++) 1988 if(tkttagset(i, m) && (iprev == nil || !tkttagset(iprev, m))) 1989 tmpstack[nt++] = m; 1990 } 1991 if(nprev > 0) { 1992 /* 1993 * Find lowest tag in stack that ends before any tag beginning here. 1994 * We have to emit end tags all the way down to there, then add 1995 * back the ones that haven't actually ended here, together with ones 1996 * that start here, and sort all of the added ones so that tags that 1997 * end later are lower in the stack. 1998 */ 1999 ii = *pnstack; 2000 for(k = *pnstack - 1; k >=0; k--) { 2001 m = stack[k]; 2002 if(i == nil || !tkttagset(i, m)) 2003 ii = k; 2004 else 2005 for(j = 0; j < nt; j++) 2006 if(tagendsbefore(tkt, ix, m, tmpstack[j])) 2007 ii = k; 2008 } 2009 for(k = *pnstack - 1; k >= ii; k--) { 2010 m = stack[k]; 2011 r = fmtprint(fmt, "</%s>", tkttagname(tkt, m)); 2012 if(r < 0) 2013 return r; 2014 /* add m back to starting tags if m didn't actually end here */ 2015 if(i != nil && tkttagset(i, m)) 2016 tmpstack[nt++] = m; 2017 } 2018 *pnstack = ii; 2019 } 2020 if(nt > 0) { 2021 /* add tags which open or reopen here */ 2022 onstack = *pnstack; 2023 k = onstack; 2024 for(j = 0; j < nt; j++) 2025 stack[k++] = tmpstack[j]; 2026 *pnstack = k; 2027 if(k - onstack > 1) { 2028 /* sort new stack entries so tags that end later are lower in stack */ 2029 for(ii = k-2; ii>= onstack; ii--) { 2030 m = stack[ii]; 2031 for(j = ii+1; j < k && tagendsbefore(tkt, ix, m, stack[j]); j++) { 2032 stack[j-1] = stack[j]; 2033 } 2034 stack[j-1] = m; 2035 } 2036 } 2037 for(j = onstack; j < k; j++) { 2038 r = fmtprint(fmt, "<%s>", tkttagname(tkt, stack[j])); 2039 if(r < 0) 2040 return r; 2041 } 2042 } 2043 return 0; 2044 } 2045 2046 /* 2047 * In 'sgml' format, just print text (no special treatment of 2048 * special characters, except that < turns into <) 2049 * interspersed with things like <Bold> and </Bold> 2050 * (where Bold is a tag name). 2051 * Make sure that the tag pairs nest properly. 2052 */ 2053 static char* 2054 tktget(TkText *tkt, TkTindex *ix1, TkTindex *ix2, int sgml, char **val) 2055 { 2056 int n, m, i, bychar, nstack; 2057 int *stack, *tmpstack; 2058 char *s; 2059 TkTitem *iprev; 2060 Tk *sub; 2061 Fmt fmt; 2062 char *buf; 2063 2064 if(!tktindbefore(ix1, ix2)) 2065 return nil; 2066 2067 stack = nil; 2068 tmpstack = nil; 2069 2070 iprev = nil; 2071 fmtstrinit(&fmt); 2072 buf = mallocz(100, 0); 2073 if(buf == nil) 2074 return TkNomem; 2075 if(sgml) { 2076 stack = malloc((tkt->nexttag+1)*sizeof(int)); 2077 tmpstack = malloc((tkt->nexttag+1)*sizeof(int)); 2078 if(stack == nil || tmpstack == nil) 2079 goto nomemret; 2080 nstack = 0; 2081 } 2082 for(;;) { 2083 if(ix1->item == ix2->item && ix1->pos == ix2->pos) 2084 break; 2085 s = nil; 2086 bychar = 0; 2087 m = 1; 2088 switch(ix1->item->kind) { 2089 case TkTrune: 2090 s = ix1->item->istring; 2091 s += tktutfpos(s, ix1->pos); 2092 if(ix1->item == ix2->item) { 2093 m = ix2->pos - ix1->pos; 2094 bychar = 1; 2095 } 2096 break; 2097 case TkTascii: 2098 s = ix1->item->istring + ix1->pos; 2099 if(ix1->item == ix2->item) { 2100 m = ix2->pos - ix1->pos; 2101 bychar = 1; 2102 } 2103 else { 2104 m = strlen(s); 2105 if(sgml && memchr(s, '<', m) != nil) 2106 bychar = 1; 2107 } 2108 break; 2109 case TkTtab: 2110 s = "\t"; 2111 break; 2112 case TkTnewline: 2113 s = "\n"; 2114 break; 2115 case TkTwin: 2116 sub = ix1->item->iwin->sub; 2117 if(sgml && sub != nil && sub->name != nil) { 2118 snprint(buf, 100, "<Window %s>", sub->name->name); 2119 s = buf; 2120 } 2121 } 2122 if(s != nil) { 2123 if(sgml) { 2124 n = tktsgmltags(tkt, &fmt, iprev, ix1->item, ix1, stack, &nstack, tmpstack); 2125 if(n < 0) 2126 goto nomemret; 2127 } 2128 if(bychar) { 2129 if (ix1->item->kind == TkTrune) 2130 n = fmtprint(&fmt, "%.*s", m, s); 2131 else { 2132 n = 0; 2133 for(i = 0; i < m && n >= 0; i++) { 2134 if(s[i] == '<') 2135 n = fmtprint(&fmt, "<"); 2136 else 2137 n = fmtprint(&fmt, "%c", s[i]); 2138 } 2139 } 2140 } 2141 else 2142 n = fmtprint(&fmt, "%s", s); 2143 if(n < 0) 2144 goto nomemret; 2145 iprev = ix1->item; 2146 } 2147 if(ix1->item == ix2->item) 2148 break; 2149 if(!tktadjustind(tkt, TkTbyitem, ix1)) { 2150 if(tktdbg) 2151 print("tktextget botch\n"); 2152 break; 2153 } 2154 } 2155 if(sgml) { 2156 n = tktsgmltags(tkt, &fmt, iprev, nil, nil, stack, &nstack, tmpstack); 2157 if(n < 0) 2158 goto nomemret; 2159 } 2160 2161 *val = fmtstrflush(&fmt); 2162 free(buf); 2163 return nil; 2164 2165 nomemret: 2166 free(buf); 2167 if(stack != nil) 2168 free(stack); 2169 if(tmpstack != nil) 2170 free(tmpstack); 2171 return TkNomem; 2172 } 2173 2174 /* Widget Commands (+ means implemented) 2175 +bbox 2176 +cget 2177 +compare 2178 +configure 2179 +debug 2180 +delete 2181 +dlineinfo 2182 +dump 2183 +get 2184 +index 2185 +insert 2186 +mark 2187 +scan 2188 +search 2189 +see 2190 +tag 2191 +window 2192 +xview 2193 +yview 2194 */ 2195 2196 static int 2197 tktviewrectclip(Rectangle *r, Rectangle b); 2198 2199 static char* 2200 tktextbbox(Tk *tk, char *arg, char **val) 2201 { 2202 char *e; 2203 int noclip, w, h; 2204 Rectangle r, rview; 2205 TkTindex ix; 2206 TkText *tkt; 2207 char buf[Tkmaxitem]; 2208 2209 e = tktindparse(tk, &arg, &ix); 2210 if(e != nil) 2211 return e; 2212 2213 noclip = 0; 2214 if(*arg != '\0') { 2215 /* extension to tk4.0: 2216 * "noclip" means don't clip to viewable area 2217 * "all" means give unclipped bbox of entire contents 2218 */ 2219 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 2220 if(strcmp(buf, "noclip") == 0) 2221 noclip = 1; 2222 else 2223 if(strcmp(buf, "all") == 0) { 2224 tkt = TKobj(TkText, tk); 2225 w = tktmaxwid(tkt->start.next); 2226 h = tkt->end.orig.y; 2227 return tkvalue(val, "0 0 %d %d", w, h); 2228 } 2229 } 2230 2231 /* 2232 * skip marks; bbox applies to characters only. 2233 * it's not defined what happens when bbox is applied to a newline char, 2234 * so we'll just let the default case sort that out. 2235 */ 2236 while (ix.item->kind == TkTmark) 2237 ix.item = ix.item->next; 2238 r = tktbbox(tk, &ix); 2239 2240 rview.min.x = 0; 2241 rview.min.y = 0; 2242 rview.max.x = tk->act.width - tk->ipad.x; 2243 rview.max.y = tk->act.height - tk->ipad.y; 2244 if(noclip || tktviewrectclip(&r, rview)) 2245 return tkvalue(val, "%d %d %d %d", r.min.x, r.min.y, 2246 r.max.x-r.min.x, r.max.y-r.min.y); 2247 return nil; 2248 } 2249 2250 /* 2251 * a supplemented rectclip, as ((0, 1), (0,1)) does not intersect ((0, 0), (5, 5)) 2252 * but for our purposes, we want it to. it's a hack. 2253 */ 2254 static int 2255 tktviewrectclip(Rectangle *rp, Rectangle b) 2256 { 2257 Rectangle *bp = &b; 2258 if((rp->min.x<bp->max.x && 2259 (bp->min.x<rp->max.x || (rp->max.x == b.min.x 2260 && rp->min.x == b.min.x)) && 2261 rp->min.y<bp->max.y && bp->min.y<rp->max.y)==0) 2262 return 0; 2263 /* They must overlap */ 2264 if(rp->min.x < bp->min.x) 2265 rp->min.x = bp->min.x; 2266 if(rp->min.y < bp->min.y) 2267 rp->min.y = bp->min.y; 2268 if(rp->max.x > bp->max.x) 2269 rp->max.x = bp->max.x; 2270 if(rp->max.y > bp->max.y) 2271 rp->max.y = bp->max.y; 2272 return 1; 2273 } 2274 2275 static Point 2276 scr2local(Tk *tk, Point p) 2277 { 2278 p = subpt(p, tkposn(tk)); 2279 p.x -= tk->borderwidth; 2280 p.y -= tk->borderwidth; 2281 return p; 2282 } 2283 2284 static char* 2285 tktextbutton1(Tk *tk, char *arg, char **val) 2286 { 2287 char *e; 2288 Point p; 2289 TkCtxt *c; 2290 TkTindex ix; 2291 TkTmarkinfo *mi; 2292 TkText *tkt = TKobj(TkText, tk); 2293 2294 USED(val); 2295 2296 e = tkxyparse(tk, &arg, &p); 2297 if(e != nil) 2298 return e; 2299 tkt->track = p; 2300 p = scr2local(tk, p); 2301 2302 tktxyind(tk, p.x, p.y, &ix); 2303 tkt->tflag &= ~TkTjustfoc; 2304 c = tk->env->top->ctxt; 2305 if(!(tk->flag&Tkdisabled) && c->tkkeygrab != tk 2306 && (tk->name != nil) && ix.item->kind != TkTwin) { 2307 tkfocus(tk->env->top, tk->name->name, nil); 2308 tkt->tflag |= TkTjustfoc; 2309 return nil; 2310 } 2311 2312 mi = tktfindmark(tkt->marks, "insert"); 2313 if(tktdbg && !mi) { 2314 print("tktextbutton1: botch\n"); 2315 return nil; 2316 } 2317 tktmarkmove(tk, mi, &ix); 2318 2319 tktclearsel(tk); 2320 tkrepeat(tk, autoselect, nil, TkRptpause, TkRptinterval); 2321 return nil; 2322 } 2323 2324 static char* 2325 tktextbutton1r(Tk *tk, char *arg, char **val) 2326 { 2327 TkText *tkt; 2328 2329 USED(arg); 2330 USED(val); 2331 2332 tkt = TKobj(TkText, tk); 2333 tkt->tflag &= ~TkTnodrag; 2334 tkcancelrepeat(tk); 2335 return nil; 2336 } 2337 2338 static char* 2339 tktextcget(Tk *tk, char *arg, char **val) 2340 { 2341 TkText *tkt; 2342 TkOptab tko[3]; 2343 2344 tkt = TKobj(TkText, tk); 2345 tko[0].ptr = tk; 2346 tko[0].optab = tkgeneric; 2347 tko[1].ptr = tkt; 2348 tko[1].optab = textopts; 2349 tko[2].ptr = nil; 2350 2351 return tkgencget(tko, arg, val, tk->env->top); 2352 } 2353 2354 static char* 2355 tktextcompare(Tk *tk, char *arg, char **val) 2356 { 2357 int op; 2358 char *e; 2359 TkTindex i1, i2; 2360 TkText *tkt; 2361 TkStab *s; 2362 char *buf; 2363 2364 tkt = TKobj(TkText, tk); 2365 2366 e = tktindparse(tk, &arg, &i1); 2367 if(e != nil) 2368 return e; 2369 2370 if(*arg == '\0') 2371 return TkBadcm; 2372 2373 buf = mallocz(Tkmaxitem, 0); 2374 if(buf == nil) 2375 return TkNomem; 2376 2377 arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); 2378 2379 op = -1; 2380 for(s = tkcompare; s->val; s++) 2381 if(strcmp(s->val, buf) == 0) { 2382 op = s->con; 2383 break; 2384 } 2385 if(op == -1) { 2386 free(buf); 2387 return TkBadcm; 2388 } 2389 2390 e = tktindparse(tk, &arg, &i2); 2391 if(e != nil) { 2392 free(buf); 2393 return e; 2394 } 2395 2396 e = tkvalue(val, tktindcompare(tkt, &i1, op, &i2)? "1" : "0"); 2397 free(buf); 2398 return e; 2399 } 2400 2401 static char* 2402 tktextconfigure(Tk *tk, char *arg, char **val) 2403 { 2404 char *e; 2405 TkGeom g; 2406 int bd; 2407 TkText *tkt; 2408 TkOptab tko[3]; 2409 tkt = TKobj(TkText, tk); 2410 tko[0].ptr = tk; 2411 tko[0].optab = tkgeneric; 2412 tko[1].ptr = tkt; 2413 tko[1].optab = textopts; 2414 tko[2].ptr = nil; 2415 2416 if(*arg == '\0') 2417 return tkconflist(tko, val); 2418 2419 g = tk->req; 2420 bd = tk->borderwidth; 2421 2422 e = tkparse(tk->env->top, arg, tko, nil); 2423 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 2424 if (tkt->propagate != BoolT) { 2425 if ((tk->flag & Tksetwidth) == 0) 2426 tk->req.width = tk->env->wzero*Textwidth; 2427 if ((tk->flag & Tksetheight) == 0) 2428 tk->req.height = tk->env->font->height*Textheight; 2429 } 2430 /* note: tkgeomchg() may also call tktfixgeom() via tktextgeom() */ 2431 tktfixgeom(tk, &tkt->start, tkt->end.prev, 0); 2432 tktextsize(tk, 0); 2433 tkgeomchg(tk, &g, bd); 2434 tktnotdrawn(tk, 0, tkt->end.orig.y, 1); 2435 2436 return e; 2437 } 2438 2439 static char* 2440 tktextdebug(Tk *tk, char *arg, char **val) 2441 { 2442 char buf[Tkmaxitem]; 2443 2444 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 2445 if(*buf == '\0') 2446 return tkvalue(val, "%s", tktdbg? "on" : "off"); 2447 else { 2448 tktdbg = (strcmp(buf, "1") == 0 || strcmp(buf, "yes") == 0); 2449 if(tktdbg) { 2450 tktprinttext(TKobj(TkText, tk)); 2451 } 2452 return nil; 2453 } 2454 } 2455 2456 static char* 2457 tktextdelete(Tk *tk, char *arg, char **val) 2458 { 2459 int sameit; 2460 char *e; 2461 TkTindex i1, i2, ip, isee; 2462 TkTline *lmin; 2463 TkText *tkt = TKobj(TkText, tk); 2464 char buf[20], *p; 2465 2466 USED(val); 2467 2468 e = tktindparse(tk, &arg, &i1); 2469 if(e != nil) 2470 return e; 2471 tktadjustind(tkt, TkTbycharstart, &i1); 2472 2473 e = tktsplititem(&i1); 2474 if(e != nil) 2475 return e; 2476 2477 if(*arg != '\0') { 2478 e = tktindparse(tk, &arg, &i2); 2479 if(e != nil) 2480 return e; 2481 } 2482 else { 2483 i2 = i1; 2484 tktadjustind(tkt, TkTbychar, &i2); 2485 } 2486 if(tktindcompare(tkt, &i1, TkGte, &i2)) 2487 return nil; 2488 2489 sameit = (i1.item == i2.item); 2490 2491 /* save possible fixup see place */ 2492 isee.line = nil; 2493 if(i2.line->orig.y + i2.line->height < tkt->deltatv.y) { 2494 /* delete completely precedes view */ 2495 tktxyind(tk, 0, 0, &isee); 2496 } 2497 2498 e = tktsplititem(&i2); 2499 if(e != nil) 2500 return e; 2501 2502 if(sameit) { 2503 /* after split, i1 should be in previous item to i2 */ 2504 ip = i2; 2505 tktadjustind(tkt, TkTbyitemback, &ip); 2506 i1.item = ip.item; 2507 } 2508 2509 lmin = tktprevwrapline(tk, i1.line); 2510 while(i1.item != i2.item) { 2511 if(i1.item->kind != TkTmark) 2512 tktremitem(tkt, &i1); 2513 /* tktremitem moves i1 to next item */ 2514 else { 2515 if(!tktadjustind(tkt, TkTbyitem, &i1)) { 2516 if(tktdbg) 2517 print("tktextdelete botch\n"); 2518 break; 2519 } 2520 } 2521 } 2522 2523 /* 2524 * guard against invalidation of index by tktfixgeom 2525 */ 2526 if (isee.line != nil) 2527 snprint(buf, sizeof(buf), "%d.%d", tktlinenum(tkt, &isee), tktlinepos(tkt, &isee)); 2528 2529 tktfixgeom(tk, lmin, i1.line, 0); 2530 tktextsize(tk, 1); 2531 if(isee.line != nil) { 2532 p = buf; 2533 tktindparse(tk, &p, &isee); 2534 tktsee(tk, &isee, 1); 2535 } 2536 return nil; 2537 } 2538 2539 static char* 2540 tktextsee(Tk *tk, char *arg, char **val) 2541 { 2542 char *e; 2543 TkTindex ix; 2544 2545 USED(val); 2546 2547 e = tktindparse(tk, &arg, &ix); 2548 if(e != nil) 2549 return e; 2550 2551 tktsee(tk, &ix, 0); 2552 return nil; 2553 } 2554 2555 static char* 2556 tktextdelins(Tk *tk, char *arg, char **val) 2557 { 2558 int m, c, skipping, wordc, n; 2559 TkTindex ix, ix2; 2560 TkText *tkt = TKobj(TkText, tk); 2561 char buf[30]; 2562 2563 USED(val); 2564 2565 if(tk->flag&Tkdisabled) 2566 return nil; 2567 2568 if(tktgetsel(tk, &ix, &ix2)) 2569 tktextdelete(tk, "sel.first sel.last", nil); 2570 else { 2571 while(*arg == ' ') 2572 arg++; 2573 if(*arg == '-') { 2574 m = arg[1]; 2575 if(m == 'c') 2576 n = 1; 2577 else { 2578 /* delete prev word (m=='w') or prev line (m=='l') */ 2579 if(!tktmarkind(tk, "insert", &ix)) 2580 return nil; 2581 if(!tktadjustind(tkt, TkTbycharback, &ix)) 2582 return nil; 2583 n = 1; 2584 /* ^W skips back over nonwordchars, then takes maximal seq of wordchars */ 2585 skipping = 1; 2586 for(;;) { 2587 c = tktindrune(&ix); 2588 if(c == '\n') { 2589 /* special case: always delete at least one char */ 2590 if(n > 1) 2591 n--; 2592 break; 2593 } 2594 if(m == 'w') { 2595 wordc = tkiswordchar(c); 2596 if(wordc && skipping) 2597 skipping = 0; 2598 else if(!wordc && !skipping) { 2599 n--; 2600 break; 2601 } 2602 } 2603 if(tktadjustind(tkt, TkTbycharback, &ix)) 2604 n++; 2605 else 2606 break; 2607 } 2608 } 2609 sprint(buf, "insert-%dc insert", n); 2610 tktextdelete(tk, buf, nil); 2611 } 2612 else 2613 tktextdelete(tk, "insert", nil); 2614 tktextsee(tk, "insert", nil); 2615 } 2616 return nil; 2617 } 2618 2619 static char* 2620 tktextdlineinfo(Tk *tk, char *arg, char **val) 2621 { 2622 char *e; 2623 TkTindex ix; 2624 TkTline *l; 2625 Point p; 2626 int vh; 2627 TkText *tkt = TKobj(TkText, tk); 2628 2629 e = tktindparse(tk, &arg, &ix); 2630 if(e != nil) 2631 return e; 2632 2633 l = ix.line; 2634 vh = tk->act.height; 2635 2636 /* get p in V space */ 2637 p = subpt(l->orig, tkt->deltatv); 2638 if(p.y+l->height < 0 || p.y >= vh) 2639 return nil; 2640 2641 return tkvalue(val, "%d %d %d %d %d", 2642 p.x, p.y, l->width, l->height, l->ascent); 2643 } 2644 2645 static char* 2646 tktextdump(Tk *tk, char *arg, char **val) 2647 { 2648 TkTline *l; 2649 TkTitem *i; 2650 Fmt fmt; 2651 TkText *tkt; 2652 TkDump tkdump; 2653 TkOptab tko[2]; 2654 TkTtaginfo *ti; 2655 TkName *names, *n; 2656 char *e, *win, *p; 2657 TkTindex ix1, ix2; 2658 int r, j, numitems; 2659 ulong fg, bg; 2660 2661 tkt = TKobj(TkText, tk); 2662 2663 2664 tkdump.sgml = 0; 2665 tkdump.metrics = 0; 2666 2667 tko[0].ptr = &tkdump; 2668 tko[0].optab = dumpopts; 2669 tko[1].ptr = nil; 2670 names = nil; 2671 e = tkparse(tk->env->top, arg, tko, &names); 2672 if(e != nil) 2673 return e; 2674 2675 if(names != nil) { /* supplied indices */ 2676 p = names->name; 2677 e = tktindparse(tk, &p, &ix1); 2678 if(e != nil) { 2679 tkfreename(names); 2680 return e; 2681 } 2682 n = names->link; 2683 if(n != nil) { 2684 p = n->name; 2685 e = tktindparse(tk, &p, &ix2); 2686 if(e != nil) { 2687 tkfreename(names); 2688 return e; 2689 } 2690 } 2691 else { 2692 ix2 = ix1; 2693 tktadjustind(tkt, TkTbychar, &ix2); 2694 } 2695 tkfreename(names); 2696 if(!tktindbefore(&ix1, &ix2)) 2697 return nil; 2698 } 2699 else 2700 return TkBadix; 2701 2702 if(tkdump.metrics != 0) { 2703 fmtstrinit(&fmt); 2704 if(fmtprint(&fmt, "%%Fonts\n") < 0) 2705 return TkNomem; 2706 for(ti=tkt->tags; ti != nil; ti=ti->next) { 2707 if(ti->env == nil || ti->env->font == nil) 2708 continue; 2709 if(fmtprint(&fmt, "%d::%s\n", ti->id,ti->env->font->name) < 0) 2710 return TkNomem; 2711 } 2712 if(fmtprint(&fmt, "-1::%s\n%%Colors\n", tk->env->font->name) < 0) 2713 return TkNomem; 2714 for(ti=tkt->tags; ti != nil; ti=ti->next) { 2715 if(ti->env == nil) 2716 continue; 2717 bg = ti->env->colors[TkCbackgnd]; 2718 fg = ti->env->colors[TkCforegnd]; 2719 if(bg == tk->env->colors[TkCbackgnd] && 2720 fg == ti->env->colors[TkCforegnd]) 2721 continue; 2722 r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, bg); 2723 if(r < 0) 2724 return TkNomem; 2725 r = fmtprint(&fmt,"%d::#%.8lux\n", ti->id, fg); 2726 if(r < 0) 2727 return TkNomem; 2728 } 2729 if(fmtprint(&fmt, "%%Lines\n") < 0) 2730 return TkNomem; 2731 2732 /* 2733 * In 'metrics' format lines are recorded in the following way: 2734 * xorig yorig wd ht as [data] 2735 * where data is of the form: 2736 * CodeWidth{tags} data 2737 * For Example; 2738 * A200{200000} Hello World! 2739 * denotes an A(scii) contiguous string of 200 pixels with 2740 * bit 20 set in its tags which corresponds to some font. 2741 * 2742 */ 2743 if(ix2.line->items != ix2.item) 2744 ix2.line = ix2.line->next; 2745 for(l = ix1.line; l != ix2.line; l = l->next) { 2746 numitems = 0; 2747 for(i = l->items; i != nil; i = i->next) { 2748 if(i->kind != TkTmark) 2749 numitems++; 2750 } 2751 r = fmtprint(&fmt, "%d %d %d %d %d %d ", 2752 l->orig.x, l->orig.y, l->width, l->height, l->ascent,numitems); 2753 if(r < 0) 2754 return TkNomem; 2755 for(i = l->items; i != nil; i = i->next) { 2756 switch(i->kind) { 2757 case TkTascii: 2758 case TkTrune: 2759 r = i->kind == TkTascii ? 'A' : 'R'; 2760 if(fmtprint(&fmt,"[%c%d{", r, i->width) < 0) 2761 return TkNomem; 2762 if(i->tags !=0 || i->tagextra !=0) { 2763 if(fmtprint(&fmt,"%lux", i->tags[0]) < 0) 2764 return TkNomem; 2765 for(j=0; j < i->tagextra; j++) 2766 if(fmtprint(&fmt,"::%lux", i->tags[j+1]) < 0) 2767 return TkNomem; 2768 } 2769 /* XXX string should be quoted to avoid embedded ']'s */ 2770 if(fmtprint(&fmt,"}%s]", i->istring) < 0) 2771 return TkNomem; 2772 break; 2773 case TkTnewline: 2774 case TkTcontline: 2775 r = i->kind == TkTnewline ? 'N' : 'C'; 2776 if(fmtprint(&fmt, "[%c]", r) < 0) 2777 return TkNomem; 2778 break; 2779 case TkTtab: 2780 if(fmtprint(&fmt,"[T%d]",i->width) < 0) 2781 return TkNomem; 2782 break; 2783 case TkTwin: 2784 win = "<null>"; 2785 if(i->iwin->sub != nil) 2786 win = i->iwin->sub->name->name; 2787 if(fmtprint(&fmt,"[W%d %s]",i->width, win) < 0) 2788 return TkNomem; 2789 break; 2790 } 2791 if(fmtprint(&fmt, " ") < 0) 2792 return TkNomem; 2793 2794 } 2795 if(fmtprint(&fmt, "\n") < 0) 2796 return TkNomem; 2797 *val = fmtstrflush(&fmt); 2798 if(*val == nil) 2799 return TkNomem; 2800 } 2801 } 2802 else 2803 return tktget(tkt, &ix1, &ix2, tkdump.sgml, val); 2804 2805 return nil; 2806 } 2807 2808 2809 static char* 2810 tktextget(Tk *tk, char *arg, char **val) 2811 { 2812 char *e; 2813 TkTindex ix1, ix2; 2814 TkText *tkt = TKobj(TkText, tk); 2815 2816 e = tktindparse(tk, &arg, &ix1); 2817 if(e != nil) 2818 return e; 2819 2820 if(*arg != '\0') { 2821 e = tktindparse(tk, &arg, &ix2); 2822 if(e != nil) 2823 return e; 2824 } 2825 else { 2826 ix2 = ix1; 2827 tktadjustind(tkt, TkTbychar, &ix2); 2828 } 2829 return tktget(tkt, &ix1, &ix2, 0, val); 2830 } 2831 2832 static char* 2833 tktextindex(Tk *tk, char *arg, char **val) 2834 { 2835 char *e; 2836 TkTindex ix; 2837 TkText *tkt = TKobj(TkText, tk); 2838 2839 e = tktindparse(tk, &arg, &ix); 2840 if(e != nil) 2841 return e; 2842 return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix), tktlinepos(tkt, &ix)); 2843 } 2844 2845 static char* 2846 tktextinsert(Tk *tk, char *arg, char **val) 2847 { 2848 int n; 2849 char *e, *p, *pe; 2850 TkTindex ins, pins; 2851 TkTtaginfo *ti; 2852 TkText *tkt; 2853 TkTline *lmin; 2854 TkTop *top; 2855 TkTitem *tagit; 2856 char *tbuf, *buf; 2857 2858 USED(val); 2859 2860 tkt = TKobj(TkText, tk); 2861 top = tk->env->top; 2862 2863 e = tktindparse(tk, &arg, &ins); 2864 if(e != nil) 2865 return e; 2866 2867 if(ins.item->kind == TkTmark) { 2868 if(ins.item->imark->gravity == Tkleft) { 2869 while(ins.item->kind == TkTmark && ins.item->imark->gravity == Tkleft) 2870 if(!tktadjustind(tkt, TkTbyitem, &ins)) { 2871 if(tktdbg) 2872 print("tktextinsert botch\n"); 2873 break; 2874 } 2875 } 2876 else { 2877 for(;;) { 2878 pins = ins; 2879 if(!tktadjustind(tkt, TkTbyitemback, &pins)) 2880 break; 2881 if(pins.item->kind == TkTmark && pins.item->imark->gravity == Tkright) 2882 ins = pins; 2883 else 2884 break; 2885 } 2886 } 2887 } 2888 2889 lmin = tktprevwrapline(tk, ins.line); 2890 2891 n = strlen(arg) + 1; 2892 if(n < Tkmaxitem) 2893 n = Tkmaxitem; 2894 tbuf = malloc(n); 2895 if(tbuf == nil) 2896 return TkNomem; 2897 buf = mallocz(Tkmaxitem, 0); 2898 if(buf == nil) { 2899 free(tbuf); 2900 return TkNomem; 2901 } 2902 2903 tagit = nil; 2904 2905 while(*arg != '\0') { 2906 arg = tkword(top, arg, tbuf, tbuf+n, nil); 2907 if(*arg != '\0') { 2908 /* tag list spec -- add some slop to tagextra for added tags */ 2909 e = tktnewitem(TkTascii, (tkt->nexttag-1)/32 + 1, &tagit); 2910 if(e != nil) { 2911 free(tbuf); 2912 free(buf); 2913 return e; 2914 } 2915 arg = tkword(top, arg, buf, buf+Tkmaxitem, nil); 2916 p = buf; 2917 while(*p) { 2918 while(*p == ' ') { 2919 p++; 2920 } 2921 if(*p == '\0') 2922 break; 2923 pe = strchr(p, ' '); 2924 if(pe != nil) 2925 *pe = '\0'; 2926 ti = tktfindtag(tkt->tags, p); 2927 if(ti == nil) { 2928 e = tktaddtaginfo(tk, p, &ti); 2929 if(e != nil) { 2930 if(tagit != nil) 2931 free(tagit); 2932 free(tbuf); 2933 free(buf); 2934 return e; 2935 } 2936 } 2937 tkttagbit(tagit, ti->id, 1); 2938 if(pe == nil) 2939 break; 2940 else 2941 p = pe+1; 2942 } 2943 } 2944 e = tktinsert(tk, &ins, tbuf, tagit); 2945 if(tagit != nil) { 2946 free(tagit); 2947 tagit = nil; 2948 } 2949 if(e != nil) { 2950 free(tbuf); 2951 free(buf); 2952 return e; 2953 } 2954 } 2955 2956 tktfixgeom(tk, lmin, ins.line, 0); 2957 tktextsize(tk, 1); 2958 2959 free(tbuf); 2960 free(buf); 2961 2962 return nil; 2963 } 2964 2965 static char* 2966 tktextinserti(Tk *tk, char *arg, char **val) 2967 { 2968 int n; 2969 TkTline *lmin; 2970 TkTindex ix, is1, is2; 2971 TkText *tkt = TKobj(TkText, tk); 2972 char *tbuf, *buf; 2973 2974 USED(val); 2975 2976 if(tk->flag&Tkdisabled) 2977 return nil; 2978 2979 buf = mallocz(Tkmaxitem, 0); 2980 if(buf == nil) 2981 return TkNomem; 2982 2983 tbuf = nil; 2984 n = strlen(arg) + 1; 2985 if(n < Tkmaxitem) 2986 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 2987 else { 2988 tbuf = malloc(n); 2989 if(tbuf == nil) { 2990 free(buf); 2991 return TkNomem; 2992 } 2993 tkword(tk->env->top, arg, tbuf, buf+n, nil); 2994 } 2995 if(*buf == '\0') 2996 goto Ret; 2997 if(!tktmarkind(tk, "insert", &ix)) { 2998 print("tktextinserti: botch\n"); 2999 goto Ret; 3000 } 3001 if(tktgetsel(tk, &is1, &is2)) { 3002 if(tktindcompare(tkt, &is1, TkLte, &ix) && 3003 tktindcompare(tkt, &is2, TkGte, &ix)) { 3004 tktextdelete(tk, "sel.first sel.last", nil); 3005 /* delete might have changed ix item */ 3006 tktmarkind(tk, "insert", &ix); 3007 } 3008 } 3009 3010 lmin = tktprevwrapline(tk, ix.line); 3011 tktinsert(tk, &ix, tbuf==nil ? buf : tbuf, 0); 3012 tktfixgeom(tk, lmin, ix.line, 0); 3013 if(tktmarkind(tk, "insert", &ix)) /* index doesn't remain valid after fixgeom */ 3014 tktsee(tk, &ix, 0); 3015 tktextsize(tk, 1); 3016 Ret: 3017 if(tbuf != nil) 3018 free(tbuf); 3019 free(buf); 3020 return nil; 3021 } 3022 3023 static char* 3024 tktextmark(Tk *tk, char *arg, char **val) 3025 { 3026 char *buf; 3027 TkCmdtab *cmd; 3028 3029 buf = mallocz(Tkmaxitem, 0); 3030 if(buf == nil) 3031 return TkNomem; 3032 arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); 3033 for(cmd = tktmarkcmd; cmd->name != nil; cmd++) { 3034 if(strcmp(cmd->name, buf) == 0) { 3035 free(buf); 3036 return cmd->fn(tk, arg, val); 3037 } 3038 } 3039 free(buf); 3040 return TkBadcm; 3041 } 3042 3043 static char* 3044 tktextscan(Tk *tk, char *arg, char **val) 3045 { 3046 char *e; 3047 int mark, x, y, xmax, ymax, vh, vw; 3048 Point p, odeltatv; 3049 char buf[Tkmaxitem]; 3050 TkText *tkt = TKobj(TkText, tk); 3051 3052 USED(val); 3053 3054 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3055 3056 if(strcmp(buf, "mark") == 0) 3057 mark = 1; 3058 else 3059 if(strcmp(buf, "dragto") == 0) 3060 mark = 0; 3061 else 3062 return TkBadcm; 3063 3064 e = tkxyparse(tk, &arg, &p); 3065 if(e != nil) 3066 return e; 3067 3068 if(mark) 3069 tkt->track = p; 3070 else { 3071 odeltatv = tkt->deltatv; 3072 vw = tk->act.width - tk->ipad.x; 3073 vh = tk->act.height - tk->ipad.y; 3074 ymax = tkt->end.prev->orig.y + tkt->end.prev->height - vh; 3075 y = tkt->deltatv.y -10*(p.y - tkt->track.y); 3076 if(y > ymax) 3077 y = ymax; 3078 if(y < 0) 3079 y = 0; 3080 tkt->deltatv.y = y; 3081 e = tktsetscroll(tk, Tkvertical); 3082 if(e != nil) 3083 return e; 3084 if(tkt->opts[TkTwrap] == Tkwrapnone) { 3085 xmax = tktmaxwid(tkt->start.next) - vw; 3086 x = tkt->deltatv.x - 10*(p.x - tkt->track.x); 3087 if(x > xmax) 3088 x = xmax; 3089 if(x < 0) 3090 x = 0; 3091 tkt->deltatv.x = x; 3092 e = tktsetscroll(tk, Tkhorizontal); 3093 if(e != nil) 3094 return e; 3095 } 3096 tktfixscroll(tk, odeltatv); 3097 tkt->track = p; 3098 } 3099 3100 return nil; 3101 } 3102 3103 static char* 3104 tktextscrollpages(Tk *tk, char *arg, char **val) 3105 { 3106 TkText *tkt = TKobj(TkText, tk); 3107 3108 USED(tkt); 3109 USED(arg); 3110 USED(val); 3111 return nil; 3112 } 3113 3114 static char* 3115 tktextsearch(Tk *tk, char *arg, char **val) 3116 { 3117 int i, n; 3118 Rune r; 3119 char *e, *s; 3120 int wrap, fwd, nocase; 3121 TkText *tkt; 3122 TkTindex ix1, ix2, ixstart, ixend, tx; 3123 char buf[Tkmaxitem]; 3124 3125 tkt = TKobj(TkText, tk); 3126 3127 fwd = 1; 3128 nocase = 0; 3129 3130 while(*arg != '\0') { 3131 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3132 if(*buf != '-') 3133 break; 3134 if(strcmp(buf, "-backwards") == 0) 3135 fwd = 0; 3136 else if(strcmp(buf, "-nocase") == 0) 3137 nocase = 1; 3138 else if(strcmp(buf, "--") == 0) { 3139 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3140 break; 3141 } 3142 } 3143 3144 tktstartind(tkt, &ixstart); 3145 tktadjustind(tkt, TkTbycharstart, &ixstart); 3146 tktendind(tkt, &ixend); 3147 3148 if(*arg == '\0') 3149 return TkOparg; 3150 3151 e = tktindparse(tk, &arg, &ix1); 3152 if(e != nil) 3153 return e; 3154 tktadjustind(tkt, fwd? TkTbycharstart : TkTbycharback, &ix1); 3155 3156 if(*arg != '\0') { 3157 wrap = 0; 3158 e = tktindparse(tk, &arg, &ix2); 3159 if(e != nil) 3160 return e; 3161 if(!fwd) 3162 tktadjustind(tkt, TkTbycharback, &ix2); 3163 } 3164 else { 3165 wrap = 1; 3166 if(fwd) { 3167 if(tktindcompare(tkt, &ix1, TkEq, &ixstart)) 3168 ix2 = ixend; 3169 else { 3170 ix2 = ix1; 3171 tktadjustind(tkt, TkTbycharback, &ix2); 3172 } 3173 } 3174 else { 3175 if(tktindcompare(tkt, &ix1, TkEq, &ixend)) 3176 ix2 = ixstart; 3177 else { 3178 ix2 = ix1; 3179 tktadjustind(tkt, TkTbychar, &ix2); 3180 } 3181 } 3182 } 3183 tktadjustind(tkt, TkTbycharstart, &ix2); 3184 if(tktindcompare(tkt, &ix1, TkEq, &ix2)) 3185 return nil; 3186 3187 if(*buf == '\0') 3188 return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1)); 3189 3190 while(!(ix1.item == ix2.item && ix1.pos == ix2.pos)) { 3191 tx = ix1; 3192 for(i = 0; buf[i] != '\0'; i++) { 3193 switch(tx.item->kind) { 3194 case TkTascii: 3195 if(!tktcmatch(tx.item->istring[tx.pos], buf[i], nocase)) 3196 goto nomatch; 3197 break; 3198 case TkTrune: 3199 s = tx.item->istring; 3200 s += tktutfpos(s, tx.pos); 3201 n = chartorune(&r, s); 3202 if(strncmp(s, buf+i, n) != 0) 3203 goto nomatch; 3204 i += n-1; 3205 break; 3206 case TkTtab: 3207 if(buf[i] != '\t') 3208 goto nomatch; 3209 break; 3210 case TkTnewline: 3211 if(buf[i] != '\n') 3212 goto nomatch; 3213 break; 3214 default: 3215 goto nomatch; 3216 } 3217 tktadjustind(tkt, TkTbychar, &tx); 3218 } 3219 return tkvalue(val, "%d.%d", tktlinenum(tkt, &ix1), tktlinepos(tkt, &ix1)); 3220 nomatch: 3221 if(fwd) { 3222 if(!tktadjustind(tkt, TkTbychar, &ix1)) { 3223 if(!wrap) 3224 break; 3225 ix1 = ixstart; 3226 } 3227 } 3228 else { 3229 if(!tktadjustind(tkt, TkTbycharback, &ix1)) { 3230 if(!wrap) 3231 break; 3232 ix1 = ixend; 3233 } 3234 } 3235 } 3236 3237 return nil; 3238 } 3239 3240 char* 3241 tktextselection(Tk *tk, char *arg, char **val) 3242 { 3243 USED(val); 3244 if (strcmp(arg, " clear") == 0) { 3245 tktclearsel(tk); 3246 return nil; 3247 } 3248 else 3249 return TkBadcm; 3250 } 3251 3252 static void 3253 doselectto(Tk *tk, Point p, int dbl) 3254 { 3255 int halfway; 3256 TkTindex cur, insert, first, last; 3257 TkText *tkt = TKobj(TkText, tk); 3258 tktclearsel(tk); 3259 3260 halfway = tktxyind(tk, p.x, p.y, &cur); 3261 3262 if(!dbl) { 3263 if(!tktmarkind(tk, "insert", &insert)) 3264 insert = cur; 3265 3266 if(tktindcompare(tkt, &cur, TkLt, &insert)) { 3267 first = cur; 3268 last = insert; 3269 } 3270 else { 3271 first = insert; 3272 last = cur; 3273 if(halfway) 3274 tktadjustind(tkt, TkTbychar, &last); 3275 if(last.line == &tkt->end) 3276 tktadjustind(tkt, TkTbycharback, &last); 3277 if(tktindcompare(tkt, &first, TkGte, &last)) 3278 return; 3279 cur = last; 3280 } 3281 tktsee(tk, &cur, 0); 3282 } 3283 else { 3284 first = cur; 3285 last = cur; 3286 tktdoubleclick(tkt, &first, &last); 3287 } 3288 3289 tkttagchange(tk, TkTselid, &first, &last, 1); 3290 } 3291 3292 static void 3293 autoselect(Tk *tk, void *v, int cancelled) 3294 { 3295 TkText *tkt = TKobj(TkText, tk); 3296 Rectangle hitr; 3297 Point p; 3298 USED(v); 3299 3300 if (cancelled) 3301 return; 3302 3303 p = scr2local(tk, tkt->track); 3304 if (tkvisiblerect(tk, &hitr) && ptinrect(p, hitr)) 3305 return; 3306 doselectto(tk, p, 0); 3307 tkdirty(tk); 3308 tkupdate(tk->env->top); 3309 } 3310 3311 static char* 3312 tktextselectto(Tk *tk, char *arg, char **val) 3313 { 3314 int dbl; 3315 char *e; 3316 Point p; 3317 Rectangle hitr; 3318 TkText *tkt = TKobj(TkText, tk); 3319 3320 USED(val); 3321 3322 if(tkt->tflag & (TkTjustfoc|TkTnodrag)) 3323 return nil; 3324 3325 e = tkxyparse(tk, &arg, &p); 3326 if(e != nil) 3327 return e; 3328 tkt->track = p; 3329 p = scr2local(tk, p); 3330 3331 arg = tkskip(arg, " "); 3332 if(*arg == 'd') { 3333 tkcancelrepeat(tk); 3334 dbl = 1; 3335 tkt->tflag |= TkTnodrag; 3336 } else { 3337 dbl = 0; 3338 if (!tkvisiblerect(tk, &hitr) || !ptinrect(p, hitr)) 3339 return nil; 3340 } 3341 doselectto(tk, p, dbl); 3342 return nil; 3343 } 3344 3345 static char tktleft1[] = "{[(<"; 3346 static char tktright1[] = "}])>"; 3347 static char tktleft2[] = "\n"; 3348 static char tktleft3[] = "\'\"`"; 3349 3350 static char *tktleft[] = {tktleft1, tktleft2, tktleft3, nil}; 3351 static char *tktright[] = {tktright1, tktleft2, tktleft3, nil}; 3352 3353 static void 3354 tktdoubleclick(TkText *tkt, TkTindex *first, TkTindex *last) 3355 { 3356 int c, i; 3357 TkTindex ix, ix2; 3358 char *r, *l, *p; 3359 3360 for(i = 0; tktleft[i] != nil; i++) { 3361 ix = *first; 3362 l = tktleft[i]; 3363 r = tktright[i]; 3364 /* try matching character to left, looking right */ 3365 ix2 = ix; 3366 if(!tktadjustind(tkt, TkTbycharback, &ix2)) 3367 c = '\n'; 3368 else 3369 c = tktindrune(&ix2); 3370 p = strchr(l, c); 3371 if(p != nil) { 3372 if(tktclickmatch(tkt, c, r[p-l], 1, &ix)) { 3373 *last = ix; 3374 if(c != '\n') 3375 tktadjustind(tkt, TkTbycharback, last); 3376 } 3377 return; 3378 } 3379 /* try matching character to right, looking left */ 3380 c = tktindrune(&ix); 3381 p = strchr(r, c); 3382 if(p != nil) { 3383 if(tktclickmatch(tkt, c, l[p-r], -1, &ix)) { 3384 *last = *first; 3385 if(c == '\n') 3386 tktadjustind(tkt, TkTbychar, last); 3387 *first = ix; 3388 if(!(c=='\n' && ix.line == tkt->start.next && ix.item == ix.line->items)) 3389 tktadjustind(tkt, TkTbychar, first); 3390 } 3391 return; 3392 } 3393 } 3394 /* try filling out word to right */ 3395 while(tkiswordchar(tktindrune(last))) { 3396 if(!tktadjustind(tkt, TkTbychar, last)) 3397 break; 3398 } 3399 /* try filling out word to left */ 3400 for(;;) { 3401 ix = *first; 3402 if(!tktadjustind(tkt, TkTbycharback, &ix)) 3403 break; 3404 if(!tkiswordchar(tktindrune(&ix))) 3405 break; 3406 *first = ix; 3407 } 3408 } 3409 3410 static int 3411 tktclickmatch(TkText *tkt, int cl, int cr, int dir, TkTindex *ix) 3412 { 3413 int c, nest, atend; 3414 3415 nest = 1; 3416 atend = 0; 3417 for(;;) { 3418 if(dir > 0) { 3419 if(atend) 3420 break; 3421 c = tktindrune(ix); 3422 atend = !tktadjustind(tkt, TkTbychar, ix); 3423 } else { 3424 if(!tktadjustind(tkt, TkTbycharback, ix)) 3425 break; 3426 c = tktindrune(ix); 3427 } 3428 if(c == cr){ 3429 if(--nest==0) 3430 return 1; 3431 }else if(c == cl) 3432 nest++; 3433 } 3434 return cl=='\n' && nest==1; 3435 } 3436 3437 /* 3438 * return the line before line l, unless word wrap is on, 3439 * (for the first word of line l), in which case return the last non-empty line before that. 3440 * tktgeom might then combine the end of that line with the start of the insertion 3441 * (unless there is a newline in the way). 3442 */ 3443 TkTline* 3444 tktprevwrapline(Tk *tk, TkTline *l) 3445 { 3446 TkTitem *i; 3447 int *opts, wrapmode; 3448 TkText *tkt = TKobj(TkText, tk); 3449 TkEnv env; 3450 3451 if(l == nil) 3452 return nil; 3453 /* some spacing depends on tags of first non-mark on display line */ 3454 for(i = l->items; i != nil; i = i->next) 3455 if(i->kind != TkTmark && i->kind != TkTcontline) 3456 break; 3457 if(i == nil || i->kind == TkTnewline) /* can't use !tkanytags(i) because it doesn't check env */ 3458 return l->prev; 3459 opts = mallocz(TkTnumopts*sizeof(int), 0); 3460 if(opts == nil) 3461 return l->prev; /* in worst case gets word wrap wrong */ 3462 tkttagopts(tk, i, opts, &env, nil, 1); 3463 wrapmode = opts[TkTwrap]; 3464 free(opts); 3465 if(wrapmode != Tkwrapword) 3466 return l->prev; 3467 if(l->prev != &tkt->start) 3468 l = l->prev; /* having been processed by tktgeom, shouldn't have extraneous marks etc */ 3469 return l->prev; 3470 } 3471 3472 static char* 3473 tktextsetcursor(Tk *tk, char *arg, char **val) 3474 { 3475 char *e; 3476 TkTindex ix; 3477 TkTmarkinfo *mi; 3478 TkText *tkt = TKobj(TkText, tk); 3479 3480 USED(val); 3481 3482 /* do clearsel here, because it can change indices */ 3483 tktclearsel(tk); 3484 3485 e = tktindparse(tk, &arg, &ix); 3486 if(e != nil) 3487 return e; 3488 3489 mi = tktfindmark(tkt->marks, "insert"); 3490 if(tktdbg && mi == nil) { 3491 print("tktextsetcursor: botch\n"); 3492 return nil; 3493 } 3494 tktmarkmove(tk, mi, &ix); 3495 tktsee(tk, &ix, 0); 3496 return nil; 3497 } 3498 3499 static char* 3500 tktexttag(Tk *tk, char *arg, char **val) 3501 { 3502 char *buf; 3503 TkCmdtab *cmd; 3504 3505 buf = mallocz(Tkmaxitem, 0); 3506 if(buf == nil) 3507 return TkNomem; 3508 arg = tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil); 3509 for(cmd = tkttagcmd; cmd->name != nil; cmd++) { 3510 if(strcmp(cmd->name, buf) == 0) { 3511 free(buf); 3512 return cmd->fn(tk, arg, val); 3513 } 3514 } 3515 free(buf); 3516 return TkBadcm; 3517 } 3518 3519 static char* 3520 tktextwindow(Tk *tk, char *arg, char **val) 3521 { 3522 char buf[Tkmaxitem]; 3523 TkCmdtab *cmd; 3524 3525 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 3526 for(cmd = tktwincmd; cmd->name != nil; cmd++) { 3527 if(strcmp(cmd->name, buf) == 0) 3528 return cmd->fn(tk, arg, val); 3529 } 3530 return TkBadcm; 3531 } 3532 3533 static char* 3534 tktextxview(Tk *tk, char *arg, char **val) 3535 { 3536 int ntot, vw; 3537 char *e; 3538 Point odeltatv; 3539 TkText *tkt = TKobj(TkText, tk); 3540 3541 odeltatv = tkt->deltatv; 3542 vw = tk->act.width - tk->ipad.x; 3543 ntot = tktmaxwid(tkt->start.next); 3544 if(ntot < tkt->deltatv.x +vw) 3545 ntot = tkt->deltatv.x + vw; 3546 e = tktview(tk, arg, val, vw, &tkt->deltatv.x, ntot, Tkhorizontal); 3547 if(e == nil) { 3548 e = tktsetscroll(tk, Tkhorizontal); 3549 if(e == nil) 3550 tktfixscroll(tk, odeltatv); 3551 } 3552 return e; 3553 } 3554 3555 static int 3556 istext(TkTline *l) 3557 { 3558 TkTitem *i; 3559 3560 for(i = l->items; i != nil; i = i->next) 3561 if(i->kind == TkTwin || i->kind == TkTmark) 3562 return 0; 3563 return 1; 3564 } 3565 3566 static void 3567 tkadjpage(Tk *tk, int ody, int *dy) 3568 { 3569 int y, a, b, d; 3570 TkTindex ix; 3571 TkTline *l; 3572 3573 d = *dy-ody; 3574 y = d > 0 ? tk->act.height : 0; 3575 tktxyind(tk, 0, y-d, &ix); 3576 if((l = ix.line) != nil && istext(l)){ 3577 a = l->orig.y; 3578 b = a+l->height; 3579 /* print("AP: %d %d %d (%d+%d)\n", a, ody+y, b, ody, y); */ 3580 if(a+2 < ody+y && ody+y < b-2){ /* partially obscured line */ 3581 if(d > 0) 3582 *dy -= ody+y-a; 3583 else 3584 *dy += b-ody; 3585 } 3586 } 3587 } 3588 3589 static char* 3590 tktextyview(Tk *tk, char *arg, char **val) 3591 { 3592 int ntot, vh, d; 3593 char *e; 3594 TkTline *l; 3595 Point odeltatv; 3596 TkTindex ix; 3597 TkText *tkt = TKobj(TkText, tk); 3598 char buf[Tkmaxitem], *v; 3599 3600 if(*arg != '\0') { 3601 v = tkitem(buf, arg); 3602 if(strcmp(buf, "-pickplace") == 0) 3603 return tktextsee(tk,v, val); 3604 if(strcmp(buf, "moveto") != 0 && strcmp(buf, "scroll") != 0) { 3605 e = tktindparse(tk, &arg, &ix); 3606 if(e != nil) 3607 return e; 3608 tktsee(tk, &ix, 1); 3609 return nil; 3610 } 3611 } 3612 odeltatv = tkt->deltatv; 3613 vh = tk->act.height; 3614 l = tkt->end.prev; 3615 ntot = l->orig.y + l->height; 3616 // if(ntot < tkt->deltatv.y + vh) 3617 // ntot = tkt->deltatv.y + vh; 3618 e = tktview(tk, arg, val, vh, &tkt->deltatv.y, ntot, Tkvertical); 3619 d = tkt->deltatv.y-odeltatv.y; 3620 if(d == vh || d == -vh) 3621 tkadjpage(tk, odeltatv.y, &tkt->deltatv.y); 3622 if(e == nil) { 3623 e = tktsetscroll(tk, Tkvertical); 3624 if(e == nil) 3625 tktfixscroll(tk, odeltatv); 3626 } 3627 return e; 3628 } 3629 static void 3630 tktextfocusorder(Tk *tk) 3631 { 3632 TkTindex ix; 3633 TkText *t; 3634 Tk *isub; 3635 3636 t = TKobj(TkText, tk); 3637 tktstartind(t, &ix); 3638 do { 3639 if(ix.item->kind == TkTwin) { 3640 isub = ix.item->iwin->sub; 3641 if(isub != nil) 3642 tkappendfocusorder(isub); 3643 } 3644 } while(tktadjustind(t, TkTbyitem, &ix)); 3645 } 3646 3647 TkCmdtab tktextcmd[] = 3648 { 3649 "bbox", tktextbbox, 3650 "cget", tktextcget, 3651 "compare", tktextcompare, 3652 "configure", tktextconfigure, 3653 "debug", tktextdebug, 3654 "delete", tktextdelete, 3655 "dlineinfo", tktextdlineinfo, 3656 "dump", tktextdump, 3657 "get", tktextget, 3658 "index", tktextindex, 3659 "insert", tktextinsert, 3660 "mark", tktextmark, 3661 "scan", tktextscan, 3662 "search", tktextsearch, 3663 "see", tktextsee, 3664 "selection", tktextselection, 3665 "tag", tktexttag, 3666 "window", tktextwindow, 3667 "xview", tktextxview, 3668 "yview", tktextyview, 3669 "tkTextButton1", tktextbutton1, 3670 "tkTextButton1R", tktextbutton1r, 3671 "tkTextDelIns", tktextdelins, 3672 "tkTextInsert", tktextinserti, 3673 "tkTextSelectTo", tktextselectto, 3674 "tkTextSetCursor", tktextsetcursor, 3675 "tkTextScrollPages", tktextscrollpages, 3676 "tkTextCursor", tktextcursor, 3677 nil 3678 }; 3679 3680 TkMethod textmethod = { 3681 "text", 3682 tktextcmd, 3683 tkfreetext, 3684 tkdrawtext, 3685 tktextgeom, 3686 nil, 3687 tktextfocusorder, 3688 tktdirty, 3689 tktrelpos, 3690 tktextevent, 3691 nil, /* XXX need to implement textsee */ 3692 tktinwindow 3693 }; 3694