1 #include "lib9.h" 2 #include "draw.h" 3 #include "tk.h" 4 #include "textw.h" 5 6 #define istring u.string 7 #define iwin u.win 8 #define imark u.mark 9 #define iline u.line 10 11 /* debugging */ 12 int tktdbg; 13 extern void tktprinttext(TkText*); 14 extern void tktprintindex(TkTindex*); 15 extern void tktprintitem(TkTitem*); 16 extern void tktprintline(TkTline*); 17 extern void tktcheck(TkText*, char*); 18 int tktutfpos(char*, int); 19 20 char* 21 tktnewitem(int kind, int tagextra,TkTitem **ret) 22 { 23 int n; 24 TkTitem *i; 25 26 n = sizeof(TkTitem) + tagextra * sizeof(ulong); 27 i = malloc(n); 28 if(i == nil) 29 return TkNomem; 30 31 memset(i, 0, n); 32 i->kind = kind; 33 i->tagextra = tagextra; 34 *ret = i; 35 return nil; 36 } 37 38 char* 39 tktnewline(int flags, TkTitem *items, TkTline *prev, TkTline *next, TkTline **ret) 40 { 41 TkTline *l; 42 TkTitem *i; 43 44 l = malloc(sizeof(TkTline)); 45 if(l == nil) 46 return TkNomem; 47 48 memset(l, 0, sizeof(TkTline)); 49 l->flags = flags; 50 l->items = items; 51 l->prev = prev; 52 l->next = next; 53 next->prev = l; 54 prev->next = l; 55 56 for(i = items; i->next != nil;) 57 i = i->next; 58 if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) 59 print("text:tktnewline botch\n"); 60 i->iline = l; 61 62 *ret = l; 63 return nil; 64 } 65 66 /* 67 * free items; freewins is 0 when the subwindows will be 68 * freed anyway as the main text widget is being destroyed. 69 */ 70 void 71 tktfreeitems(TkText *tkt, TkTitem *i, int freewins) 72 { 73 TkTitem *n; 74 Tk *tk; 75 76 while(i != nil) { 77 n = i->next; 78 if(tkt->mouse == i) 79 tkt->mouse = nil; 80 switch(i->kind) { 81 case TkTascii: 82 case TkTrune: 83 if(i->istring != nil) 84 free(i->istring); 85 break; 86 case TkTwin: 87 if (i->iwin != nil) { 88 tk = i->iwin->sub; 89 if (tk != nil) { 90 tk->geom = nil; 91 tk->destroyed = nil; 92 if (i->iwin->owned && freewins) { 93 if (tk->name != nil) 94 tkdestroy(tk->env->top, tk->name->name, nil); 95 } else { 96 tk->parent = nil; 97 tk->geom = nil; 98 tk->destroyed = nil; 99 } 100 } 101 if(i->iwin->create != nil) 102 free(i->iwin->create); 103 free(i->iwin); 104 } 105 break; 106 case TkTmark: 107 break; 108 } 109 free(i); 110 i = n; 111 } 112 } 113 114 void 115 tktfreelines(TkText *tkt, TkTline *l, int freewins) 116 { 117 TkTline *n; 118 119 while(l != nil) { 120 n = l->next; 121 tktfreeitems(tkt, l->items, freewins); 122 free(l); 123 l = n; 124 } 125 } 126 127 void 128 tktfreetabs(TkTtabstop *t) 129 { 130 TkTtabstop *n; 131 132 while(t != nil) { 133 n = t->next; 134 free(t); 135 t = n; 136 } 137 } 138 139 void 140 tkfreetext(Tk *tk) 141 { 142 TkText *tkt = TKobj(TkText, tk); 143 144 if(tkt->start.next != nil && tkt->start.next != &(tkt->end)) { 145 tkt->end.prev->next = nil; 146 tktfreelines(tkt, tkt->start.next, 0); 147 } 148 tktfreeitems(tkt, tkt->start.items, 0); 149 tktfreeitems(tkt, tkt->end.items, 0); 150 tktfreetabs(tkt->tabs); 151 if(tkt->tagshare == nil) 152 tktfreetags(tkt->tags); 153 else 154 tk->binds = nil; 155 tktfreemarks(tkt->marks); 156 if(tkt->xscroll != nil) 157 free(tkt->xscroll); 158 if(tkt->yscroll != nil) 159 free(tkt->yscroll); 160 /* don't free image because it belongs to window */ 161 } 162 163 /* 164 * Remove the item at ix, joining previous and next items. 165 * If item is at end of line, remove next line and join 166 * its items to this one (except at end). 167 * On return, ix is adjusted to point to the next item. 168 */ 169 void 170 tktremitem(TkText *tkt, TkTindex *ix) 171 { 172 TkTline *l, *lnext; 173 TkTindex prev, nx; 174 TkTitem *i, *ilast; 175 176 l = ix->line; 177 i = ix->item; 178 179 if(i->next == nil) { 180 if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) { 181 print("tktremitem: botch 1\n"); 182 return; 183 } 184 lnext = l->next; 185 if(lnext == &tkt->end) 186 /* not supposed to remove final newline */ 187 return; 188 if(i->kind == TkTnewline) 189 tkt->nlines--; 190 ilast = tktlastitem(lnext->items); 191 ilast->iline = l; 192 i->next = lnext->items; 193 l->flags = (l->flags & ~TkTlast) | (lnext->flags & TkTlast); 194 l->next = lnext->next; 195 lnext->next->prev = l; 196 free(lnext); 197 } 198 if(l->items == i) 199 l->items = i->next; 200 else { 201 prev = *ix; 202 if(!tktadjustind(tkt, TkTbyitemback, &prev) && tktdbg) { 203 print("tktremitem: botch 2\n"); 204 return; 205 } 206 prev.item->next = i->next; 207 } 208 ix->item = i->next; 209 ix->pos = 0; 210 i->next = nil; 211 nx = *ix; 212 tktadjustind(tkt, TkTbycharstart, &nx); 213 214 /* check against cached items */ 215 if(tkt->selfirst == i) 216 tkt->selfirst = nx.item; 217 if(tkt->sellast == i) 218 tkt->sellast = nx.item; 219 if(tkt->selfirst == tkt->sellast) { 220 tkt->selfirst = nil; 221 tkt->sellast = nil; 222 } 223 224 tktfreeitems(tkt, i, 1); 225 } 226 227 int 228 tktdispwidth(Tk *tk, TkTtabstop *tb, TkTitem *i, Font *f, int x, int pos, int nchars) 229 { 230 int w, del, locked; 231 TkTtabstop *tbprev; 232 Display *d; 233 TkText *tkt; 234 TkEnv env; 235 236 tkt = TKobj(TkText, tk); 237 d = tk->env->top->display; 238 if (tb == nil) 239 tb = tkt->tabs; 240 241 switch(i->kind) { 242 case TkTrune: 243 pos = tktutfpos(i->istring, pos); 244 /* FALLTHRU */ 245 case TkTascii: 246 if(f == nil) { 247 if(!tktanytags(i)) 248 f = tk->env->font; 249 else { 250 tkttagopts(tk, i, nil, &env, nil, 1); 251 f = env.font; 252 } 253 } 254 locked = 0; 255 if(!(tkt->tflag&TkTdlocked)) 256 locked = lockdisplay(d); 257 if(nchars >= 0) 258 w = stringnwidth(f, i->istring+pos, nchars); 259 else 260 w = stringwidth(f, i->istring+pos); 261 if(locked) 262 unlockdisplay(d); 263 break; 264 case TkTtab: 265 if(tb == nil) 266 w = 0; 267 else { 268 tbprev = nil; 269 while(tb->pos <= x && tb->next != nil) { 270 tbprev = tb; 271 tb = tb->next; 272 } 273 w = tb->pos - x; 274 if(w <= 0) { 275 del = tb->pos; 276 if(tbprev != nil) 277 del -= tbprev->pos; 278 while(w <= 0) 279 w += del; 280 } 281 /* todo: other kinds of justification */ 282 } 283 break; 284 case TkTwin: 285 if(i->iwin->sub == 0) 286 w = 0; 287 else 288 w = i->iwin->sub->act.width + 2*i->iwin->padx + 2*i->iwin->sub->borderwidth; 289 break; 290 default: 291 w = 0; 292 } 293 return w; 294 } 295 296 int 297 tktindrune(TkTindex *ix) 298 { 299 int ans; 300 Rune r; 301 302 switch(ix->item->kind) { 303 case TkTascii: 304 ans = ix->item->istring[ix->pos]; 305 break; 306 case TkTrune: 307 chartorune(&r, ix->item->istring + tktutfpos(ix->item->istring, ix->pos)); 308 ans = r; 309 break; 310 case TkTtab: 311 ans = '\t'; 312 break; 313 case TkTnewline: 314 ans = '\n'; 315 break; 316 default: 317 /* only care that it isn't a word char */ 318 ans = 0x80; 319 } 320 return ans; 321 } 322 323 TkTitem* 324 tktlastitem(TkTitem *i) 325 { 326 while(i->next != nil) 327 i = i->next; 328 if(tktdbg && !(i->kind == TkTnewline || i->kind == TkTcontline)) 329 print("text:tktlastitem botch\n"); 330 331 return i; 332 } 333 334 TkTline* 335 tktitemline(TkTitem *i) 336 { 337 i = tktlastitem(i); 338 return i->iline; 339 } 340 341 int 342 tktlinenum(TkText *tkt, TkTindex *p) 343 { 344 int n; 345 TkTline *l; 346 347 if(p->line->orig.y <= tkt->end.orig.y / 2) { 348 /* line seems closer to beginning */ 349 n = 1; 350 for(l = tkt->start.next; l != p->line; l = l->next) { 351 if(tktdbg && l->next == nil) { 352 print("text: tktlinenum botch\n"); 353 break; 354 } 355 if(l->flags & TkTlast) 356 n++; 357 } 358 } 359 else { 360 n = tkt->nlines; 361 for(l = tkt->end.prev; l != p->line; l = l->prev) { 362 if(tktdbg && l->prev == nil) { 363 print("text: tktlinenum botch\n"); 364 break; 365 } 366 if(l->flags & TkTfirst) 367 n--; 368 } 369 } 370 return n; 371 } 372 373 int 374 tktlinepos(TkText *tkt, TkTindex *p) 375 { 376 int n; 377 TkTindex ix; 378 TkTitem *i; 379 380 n = 0; 381 ix = *p; 382 i = ix.item; 383 tktadjustind(tkt, TkTbylinestart, &ix); 384 while(ix.item != i) { 385 if(tktdbg && ix.item->next == nil && (ix.line->flags&TkTlast)) { 386 print("text: tktlinepos botch\n"); 387 break; 388 } 389 n += tktposcount(ix.item); 390 if(!tktadjustind(tkt, TkTbyitem, &ix)) { 391 if(tktdbg) 392 print("tktlinepos botch\n"); 393 break; 394 } 395 } 396 return (n+p->pos); 397 } 398 399 int 400 tktposcount(TkTitem *i) 401 { 402 int n; 403 404 if(i->kind == TkTascii) 405 n = strlen(i->istring); 406 else 407 if(i->kind == TkTrune) 408 n = utflen(i->istring); 409 else 410 if(i->kind == TkTmark || i->kind == TkTcontline) 411 n = 0; 412 else 413 n = 1; 414 return n; 415 } 416 417 /* 418 * Insert item i before position ins. 419 * If i is a newline or a contline, make a new line to contain the items up to 420 * and including the new newline, and make the original line 421 * contain the items from ins on. 422 * Adjust ins so that it points just after inserted item. 423 */ 424 char* 425 tktiteminsert(TkText *tkt, TkTindex *ins, TkTitem *i) 426 { 427 int hasprev, flags; 428 char *e; 429 TkTindex prev; 430 TkTline *l; 431 TkTitem *items; 432 433 prev = *ins; 434 hasprev = tktadjustind(tkt, TkTbyitemback, &prev); 435 436 if(i->kind == TkTnewline || i->kind == TkTcontline) { 437 i->next = nil; 438 if(hasprev && prev.line == ins->line) { 439 items = ins->line->items; 440 prev.item->next = i; 441 } 442 else 443 items = i; 444 445 flags = ins->line->flags&TkTfirst; 446 if(i->kind == TkTnewline) 447 flags |= TkTlast; 448 e = tktnewline(flags, items, ins->line->prev, ins->line, &l); 449 if(e != nil) { 450 if(hasprev && prev.line == ins->line) 451 prev.item->next = ins->item; 452 return e; 453 } 454 455 if(i->kind == TkTnewline) 456 ins->line->flags |= TkTfirst; 457 458 if(i->kind == TkTcontline) 459 ins->line->flags &= ~TkTfirst; 460 ins->line->items = ins->item; 461 ins->pos = 0; 462 } 463 else { 464 if(hasprev && prev.line == ins->line) 465 prev.item->next = i; 466 else 467 ins->line->items = i; 468 i->next = ins->item; 469 } 470 471 return nil; 472 } 473 474 /* 475 * If index p doesn't point at the beginning of an item, 476 * split the item at p. Adjust p to point to the beginning of 477 * the item after the split (same character it used to point at). 478 * If there is a split, the old item gets the characters before 479 * the split, and a new item gets the characters after it. 480 */ 481 char* 482 tktsplititem(TkTindex *p) 483 { 484 int l1, l2; 485 char *s1, *s2, *e; 486 TkTitem *i, *i2; 487 488 i = p->item; 489 490 if(p->pos != 0) { 491 /* 492 * Must be TkTascii or TkTrune 493 * 494 * Make new item i2, to be inserted after i, 495 * with portion of string from p->pos on 496 */ 497 498 if (i->kind == TkTascii) 499 l1 = p->pos; 500 else 501 l1 = tktutfpos(i->istring, p->pos); 502 l2 = strlen(i->istring) - l1; 503 if (l2 == 0) 504 print("tktsplititem botch\n"); 505 s1 = malloc(l1+1); 506 if(s1 == nil) 507 return TkNomem; 508 s2 = malloc(l2+1); 509 if(s2 == nil) { 510 free(s1); 511 return TkNomem; 512 } 513 514 memmove(s1, i->istring, l1); 515 s1[l1] = '\0'; 516 memmove(s2, i->istring + l1, l2); 517 s2[l2] = '\0'; 518 519 e = tktnewitem(i->kind, i->tagextra, &i2); 520 if(e != nil) { 521 free(s1); 522 free(s2); 523 return e; 524 } 525 526 free(i->istring); 527 528 tkttagcomb(i2, i, 1); 529 i2->next = i->next; 530 i->next = i2; 531 i->istring = s1; 532 i2->istring = s2; 533 534 p->item = i2; 535 p->pos = 0; 536 } 537 538 return nil; 539 } 540 541 int 542 tktmaxwid(TkTline *l) 543 { 544 int w, maxw; 545 546 maxw = 0; 547 while(l != nil) { 548 w = l->width; 549 if(w > maxw) 550 maxw = w; 551 l = l->next; 552 } 553 return maxw; 554 } 555 556 Rectangle 557 tktbbox(Tk *tk, TkTindex *ix) 558 { 559 Rectangle r; 560 int d, w; 561 TkTitem *i; 562 TkTline *l; 563 TkEnv env; 564 TkTtabstop *tb = nil; 565 Tk *sub; 566 TkText *tkt = TKobj(TkText, tk); 567 int opts[TkTnumopts]; 568 569 l = ix->line; 570 571 /* r in V space */ 572 r.min = subpt(l->orig, tkt->deltatv); 573 r.min.y += l->ascent; 574 575 /* tabs dependon tags of first non-mark on display line */ 576 for(i = l->items; i->kind == TkTmark; ) 577 i = i->next; 578 tkttagopts(tk, i, opts, &env, &tb, 1); 579 580 for(i = l->items; i != nil; i = i->next) { 581 if(i == ix->item) { 582 tkttagopts(tk, i, opts, &env, nil, 1); 583 r.min.y -= opts[TkToffset]; 584 switch(i->kind) { 585 case TkTascii: 586 case TkTrune: 587 d = tktdispwidth(tk, tb, i, nil, r.min.x, 0, ix->pos); 588 w = tktdispwidth(tk, tb, i, nil, r.min.x, ix->pos, 1); 589 r.min.x += d; 590 r.min.y -= env.font->ascent; 591 r.max.x = r.min.x + w; 592 r.max.y = r.min.y + env.font->height; 593 break; 594 case TkTwin: 595 sub = i->iwin->sub; 596 if(sub == nil) 597 break; 598 r.min.x += sub->act.x; 599 r.min.y += sub->act.y; 600 r.max.x = r.min.x + sub->act.width + 2*sub->borderwidth; 601 r.max.y = r.min.y + sub->act.height + 2*sub->borderwidth; 602 break; 603 case TkTnewline: 604 r.max.x = r.min.x; 605 r.min.y -= l->ascent; 606 r.max.y = r.min.y + l->height; 607 break; 608 default: 609 d = tktdispwidth(tk, tb, i, nil, r.min.x, 0, -1); 610 r.max.x = r.min.x + d; 611 r.max.y = r.min.y; 612 break; 613 } 614 return r; 615 } 616 r.min.x += tktdispwidth(tk, tb, i, nil, r.min.x, 0, -1); 617 } 618 r.min.x = 0; 619 r.min.y = 0; 620 r.max.x = 0; 621 r.max.y = 0; 622 return r; 623 } 624 625 /* Return left-at-baseline position of given item, in V coords */ 626 static Point 627 tktitempos(Tk *tk, TkTindex *ix) 628 { 629 Point p; 630 TkTitem *i; 631 TkTline *l; 632 TkText *tkt = TKobj(TkText, tk); 633 634 l = ix->line; 635 636 /* p in V space */ 637 p = subpt(l->orig, tkt->deltatv); 638 p.y += l->ascent; 639 640 for(i = l->items; i != nil && i != ix->item; i = i->next) 641 p.x += i->width; 642 return p; 643 } 644 645 static Tk* 646 tktdeliver(Tk *tk, TkTitem *i, TkTitem *tagit, int event, void *data, Point deltasv) 647 { 648 Tk *ftk, *dest; 649 TkTwind *w; 650 TkText *tkt; 651 TkTtaginfo *t; 652 TkTline *l; 653 TkMouse m; 654 Point mp, p; 655 TkTindex ix; 656 int bd; 657 658 dest = nil; 659 if(i != nil) { 660 tkt = TKobj(TkText, tk); 661 662 if(i->kind == TkTwin) { 663 w = i->iwin; 664 if(w->sub != nil) { 665 if(!(event & TkKey) && (event & TkEmouse)) { 666 m = *(TkMouse*)data; 667 mp.x = m.x; 668 mp.y = m.y; 669 ix.item = i; 670 ix.pos = 0; 671 ix.line = tktitemline(i); 672 p = tktitempos(tk, &ix); 673 bd = w->sub->borderwidth; 674 mp.x = m.x - (deltasv.x + p.x + w->sub->act.x + bd); 675 mp.y = m.y - (deltasv.y + p.y + w->sub->act.y + bd); 676 ftk = tkinwindow(w->sub, mp, 0); 677 if(ftk != w->focus) { 678 tkdeliver(w->focus, TkLeave, data); 679 tkdeliver(ftk, TkEnter, data); 680 681 w->focus = ftk; 682 } 683 if(ftk != nil) 684 dest = tkdeliver(ftk, event, &m); 685 } 686 else { 687 if ((event & TkLeave) && (w->focus != w->sub)) { 688 tkdeliver(w->focus, TkLeave, data); 689 w->focus = nil; 690 event &= ~TkLeave; 691 } 692 if (event) 693 tkdeliver(w->sub, event, data); 694 } 695 if(Dx(w->sub->dirty) > 0) { 696 l = tktitemline(i); 697 tktfixgeom(tk, tktprevwrapline(tk, l), l, 0); 698 } 699 if(event & TkKey) 700 return dest; 701 } 702 } 703 704 if(tagit != 0) { 705 for(t = tkt->tags; t != nil; t = t->next) { 706 if(t->binds != nil && tkttagset(tagit, t->id)) { 707 if(tksubdeliver(tk, t->binds, event, data, 0) == TkDbreak) { 708 return dest; 709 } 710 } 711 } 712 } 713 } 714 return dest; 715 } 716 717 Tk* 718 tktinwindow(Tk *tk, Point *p) 719 { 720 TkTindex ix; 721 Point q; 722 Tk *sub; 723 724 tktxyind(tk, p->x, p->y, &ix); 725 if (ix.item == nil || ix.item->kind != TkTwin || ix.item->iwin->sub == nil) 726 return tk; 727 sub = ix.item->iwin->sub; 728 q = tktitempos(tk, &ix); 729 p->x -= q.x + sub->borderwidth + sub->act.x; 730 p->y -= q.y + sub->borderwidth + sub->act.y; 731 return sub; 732 } 733 734 Tk* 735 tktextevent(Tk *tk, int event, void *data) 736 { 737 char *e; 738 TkMouse m, vm; 739 TkTitem *f, *tagit; 740 TkText *tkt; 741 TkTindex ix; 742 Tk *dest; 743 Point deltasv; 744 745 tkt = TKobj(TkText, tk); 746 deltasv = tkposn(tk); 747 deltasv.x += tk->borderwidth + tk->ipad.x/2; 748 deltasv.y += tk->borderwidth + tk->ipad.y/2; 749 750 dest = nil; 751 if(event == TkLeave && tkt->mouse != nil) { 752 vm.x = 0; 753 vm.y = 0; 754 tktdeliver(tk, tkt->mouse, tkt->mouse, TkLeave, data, deltasv); 755 tkt->mouse = nil; 756 } 757 else if((event & TkKey) == 0 && (event & TkEmouse)) { 758 /* m in S space, tm in V space */ 759 m = *(TkMouse*)data; 760 vm = m; 761 vm.x -= deltasv.x; 762 vm.y -= deltasv.y; 763 if((event & TkMotion) == 0 || m.b == 0) { 764 tkt->current.x = vm.x; 765 tkt->current.y = vm.y; 766 } 767 tktxyind(tk, vm.x, vm.y, &ix); 768 f = ix.item; 769 if(tkt->mouse != f) { 770 tagit = nil; 771 if(tkt->mouse != nil) { 772 if(tktanytags(tkt->mouse)) { 773 e = tktnewitem(TkTascii, tkt->mouse->tagextra, &tagit); 774 if(e != nil) 775 return dest; /* XXX propagate error? */ 776 tkttagcomb(tagit, tkt->mouse, 1); 777 tkttagcomb(tagit, f, -1); 778 } 779 tktdeliver(tk, tkt->mouse, tagit, TkLeave, data, deltasv); 780 if(tagit) 781 free(tagit); 782 tagit = nil; 783 } 784 if(tktanytags(f)) { 785 e = tktnewitem(TkTascii, f->tagextra, &tagit); 786 if(e != nil) 787 return dest; /* XXX propagate error? */ 788 tkttagcomb(tagit, f, 1); 789 if(tkt->mouse) 790 tkttagcomb(tagit, tkt->mouse, -1); 791 } 792 tktdeliver(tk, f, tagit, TkEnter, data, deltasv); 793 tkt->mouse = f; 794 if(tagit) 795 free(tagit); 796 } 797 if(tkt->mouse != nil) 798 dest = tktdeliver(tk, tkt->mouse, tkt->mouse, event, &m, deltasv); 799 } 800 else if(event == TkFocusin) 801 tktextcursor(tk, " insert", (char **) nil); 802 /* pass all "real" events on to parent text widget - DBK */ 803 tksubdeliver(tk, tk->binds, event, data, 0); 804 return dest; 805 } 806 807 /* Debugging */ 808 void 809 tktprintitem(TkTitem *i) 810 { 811 int j; 812 813 print("%p:", i); 814 switch(i->kind){ 815 case TkTascii: 816 print("\"%s\"", i->istring); 817 break; 818 case TkTrune: 819 print("<rune:%s>", i->istring); 820 break; 821 case TkTnewline: 822 print("<nl:%p>", i->iline); 823 break; 824 case TkTcontline: 825 print("<cont:%p>", i->iline); 826 break; 827 case TkTtab: 828 print("<tab>"); 829 break; 830 case TkTmark: 831 print("<mk:%s>", i->imark->name); 832 break; 833 case TkTwin: 834 if (i->iwin->sub->name != nil) 835 print("<win:%s>", i->iwin->sub? i->iwin->sub->name->name : "<null>"); 836 } 837 print("[%d]", i->width); 838 if(i->tags !=0 || i->tagextra !=0) { 839 print("{%lux", i->tags[0]); 840 for(j=0; j < i->tagextra; j++) 841 print(" %lux", i->tags[j+1]); 842 print("}"); 843 } 844 print(" "); 845 } 846 847 void 848 tktprintline(TkTline *l) 849 { 850 TkTitem *i; 851 852 print("line %p: orig=(%d,%d), w=%d, h=%d, a=%d, f=%x\n\t", 853 l, l->orig.x, l->orig.y, l->width, l->height, l->ascent, l->flags); 854 for(i = l->items; i != nil; i = i->next) 855 tktprintitem(i); 856 print("\n"); 857 } 858 859 void 860 tktprintindex(TkTindex *ix) 861 { 862 print("line=%p,item=%p,pos=%d\n", ix->line, ix->item, ix->pos); 863 } 864 865 void 866 tktprinttext(TkText *tkt) 867 { 868 TkTline *l; 869 TkTtaginfo *ti; 870 TkTmarkinfo *mi; 871 872 for(ti=tkt->tags; ti != nil; ti=ti->next) 873 print("%s{%d} ", ti->name, ti->id); 874 print("\n"); 875 for(mi = tkt->marks; mi != nil; mi=mi->next) 876 print("%s{%p} ", mi->name? mi->name : "nil", mi->cur); 877 print("\n"); 878 print("selfirst=%p sellast=%p\n", tkt->selfirst, tkt->sellast); 879 880 for(l = &tkt->start; l != nil; l = l->next) 881 tktprintline(l); 882 } 883 884 /* 885 * Check that assumed invariants are true. 886 * 887 * - start line and end line have no items 888 * - all other lines have at least one item 889 * - start line leads to end line via next pointers 890 * - prev pointers point to previous lines 891 * - each line ends in either a TkTnewline or a TkTcontline 892 * whose iline pointer points to the line itself 893 * - TkTcontline and TkTmark items have no tags 894 * (this is so they don't get realloc'd because of tag combination) 895 * - all cur fields of marks point to nil or a TkTmark 896 * - selfirst and sellast correctly define select region 897 * - nlines counts the number of lines 898 */ 899 void 900 tktcheck(TkText *tkt, char *fun) 901 { 902 int nl, insel, selfound; 903 TkTline *l; 904 TkTitem *i; 905 TkTmarkinfo *mi; 906 TkTindex ix; 907 char *prob; 908 909 prob = nil; 910 nl = 0; 911 912 if(tkt->start.items != nil || tkt->end.items != nil) 913 prob = "start/end has items"; 914 for(l = tkt->start.next; l != &tkt->end; l = l->next) { 915 if(l->prev->next != l) { 916 prob = "prev mismatch"; 917 break; 918 } 919 if(l->next->prev != l) { 920 prob = "next mismatch"; 921 break; 922 } 923 i = l->items; 924 if(i == nil) { 925 prob = "empty line"; 926 break; 927 } 928 while(i->next != nil) { 929 if(i->kind == TkTnewline || i->kind == TkTcontline) { 930 prob = "premature end of line"; 931 break; 932 } 933 if(i->kind == TkTmark && (i->tags[0] != 0 || i->tagextra != 0)) { 934 prob = "mark has tags"; 935 break; 936 } 937 i = i->next; 938 } 939 if(i->kind == TkTnewline) 940 nl++; 941 if(!(i->kind == TkTnewline || i->kind == TkTcontline)) { 942 prob = "bad end of line"; 943 break; 944 } 945 if(i->kind == TkTcontline && (i->tags[0] != 0 || i->tagextra != 0)) { 946 prob = "contline has tags"; 947 break; 948 } 949 if(i->iline != l) { 950 prob = "bad end-of-line pointer"; 951 break; 952 } 953 } 954 for(mi = tkt->marks; mi != nil; mi=mi->next) { 955 if(mi->cur != nil) { 956 tktstartind(tkt, &ix); 957 do { 958 if(ix.item->kind == TkTmark && ix.item == mi->cur) 959 goto foundmark; 960 } while(tktadjustind(tkt, TkTbyitem, &ix)); 961 prob = "bad mark cur"; 962 break; 963 foundmark: ; 964 } 965 } 966 insel = 0; 967 selfound = 0; 968 tktstartind(tkt, &ix); 969 do { 970 i = ix.item; 971 if(i == tkt->selfirst) { 972 if(i->kind == TkTmark || i->kind == TkTcontline) { 973 prob = "selfirst not on character"; 974 break; 975 } 976 if(i == tkt->sellast) { 977 prob = "selfirst==sellast, not nil"; 978 break; 979 } 980 insel = 1; 981 selfound = 1; 982 } 983 if(i == tkt->sellast) { 984 if(i->kind == TkTmark || i->kind == TkTcontline) { 985 prob = "sellast not on character"; 986 break; 987 } 988 insel = 0; 989 } 990 if(i->kind != TkTmark && i->kind != TkTcontline) { 991 if(i->tags[0] & (1<<TkTselid)) { 992 if(!insel) { 993 prob = "sel set outside selfirst..sellast"; 994 break; 995 } 996 } 997 else { 998 if(insel) { 999 prob = "sel not set inside selfirst..sellast"; 1000 break; 1001 } 1002 } 1003 } 1004 } while(tktadjustind(tkt, TkTbyitem, &ix)); 1005 if(tkt->selfirst != nil && !selfound) 1006 prob = "selfirst not found"; 1007 1008 if(prob != nil) { 1009 print("tktcheck problem: %s: %s\n", fun, prob); 1010 tktprinttext(tkt); 1011 abort(); 1012 } 1013 } 1014 1015 int 1016 tktutfpos(char *s, int pos) 1017 { 1018 char *s1; 1019 int c; 1020 Rune r; 1021 1022 for (s1 = s; pos > 0; pos--) { 1023 c = *(uchar *)s1; 1024 if (c < Runeself) { 1025 if (c == '\0') 1026 break; 1027 s1++; 1028 } 1029 else 1030 s1 += chartorune(&r, s1); 1031 } 1032 return s1 - s; 1033 } 1034 1035 /* 1036 struct timerec { 1037 char *name; 1038 ulong ms; 1039 }; 1040 1041 static struct timerec tt[100]; 1042 static int ntt = 1; 1043 1044 int 1045 tktalloctime(char *name) 1046 { 1047 if(ntt >= 100) 1048 abort(); 1049 tt[ntt].name = strdup(name); 1050 tt[ntt].ms = 0; 1051 return ntt++; 1052 } 1053 1054 void 1055 tktstarttime(int ind) 1056 { 1057 return; 1058 tt[ind].ms -= osmillisec(); 1059 } 1060 1061 void 1062 tktendtime(int ind) 1063 { 1064 return; 1065 tt[ind].ms += osmillisec(); 1066 } 1067 1068 void 1069 tktdumptime(void) 1070 { 1071 int i; 1072 1073 for(i = 1; i < ntt; i++) 1074 print("%s: %d\n", tt[i].name, tt[i].ms); 1075 } 1076 */ 1077