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 r.max = r.min; 575 576 /* tabs dependon tags of first non-mark on display line */ 577 for(i = l->items; i->kind == TkTmark; ) 578 i = i->next; 579 tkttagopts(tk, i, opts, &env, &tb, 1); 580 581 for(i = l->items; i != nil; i = i->next) { 582 if(i == ix->item) { 583 tkttagopts(tk, i, opts, &env, nil, 1); 584 r.min.y -= opts[TkToffset]; 585 switch(i->kind) { 586 case TkTascii: 587 case TkTrune: 588 d = tktdispwidth(tk, tb, i, nil, r.min.x, 0, ix->pos); 589 w = tktdispwidth(tk, tb, i, nil, r.min.x, ix->pos, 1); 590 r.min.x += d; 591 r.min.y -= env.font->ascent; 592 r.max.x = r.min.x + w; 593 r.max.y = r.min.y + env.font->height; 594 break; 595 case TkTwin: 596 sub = i->iwin->sub; 597 if(sub == nil) 598 break; 599 r.min.x += sub->act.x; 600 r.min.y += sub->act.y; 601 r.max.x = r.min.x + sub->act.width + 2*sub->borderwidth; 602 r.max.y = r.min.y + sub->act.height + 2*sub->borderwidth; 603 break; 604 case TkTnewline: 605 r.max.x = r.min.x; 606 r.min.y -= l->ascent; 607 r.max.y = r.min.y + l->height; 608 break; 609 default: 610 d = tktdispwidth(tk, tb, i, nil, r.min.x, 0, -1); 611 r.max.x = r.min.x + d; 612 r.max.y = r.min.y; 613 break; 614 } 615 return r; 616 } 617 r.min.x += tktdispwidth(tk, tb, i, nil, r.min.x, 0, -1); 618 } 619 r.min.x = 0; 620 r.min.y = 0; 621 r.max.x = 0; 622 r.max.y = 0; 623 return r; 624 } 625 626 /* Return left-at-baseline position of given item, in V coords */ 627 static Point 628 tktitempos(Tk *tk, TkTindex *ix) 629 { 630 Point p; 631 TkTitem *i; 632 TkTline *l; 633 TkText *tkt = TKobj(TkText, tk); 634 635 l = ix->line; 636 637 /* p in V space */ 638 p = subpt(l->orig, tkt->deltatv); 639 p.y += l->ascent; 640 641 for(i = l->items; i != nil && i != ix->item; i = i->next) 642 p.x += i->width; 643 return p; 644 } 645 646 static Tk* 647 tktdeliver(Tk *tk, TkTitem *i, TkTitem *tagit, int event, void *data, Point deltasv) 648 { 649 Tk *ftk, *dest; 650 TkTwind *w; 651 TkText *tkt; 652 TkTtaginfo *t; 653 TkTline *l; 654 TkMouse m; 655 Point mp, p; 656 TkTindex ix; 657 int bd; 658 659 dest = nil; 660 if(i != nil) { 661 tkt = TKobj(TkText, tk); 662 663 if(i->kind == TkTwin) { 664 w = i->iwin; 665 if(w->sub != nil) { 666 if(!(event & TkKey) && (event & TkEmouse)) { 667 m = *(TkMouse*)data; 668 mp.x = m.x; 669 mp.y = m.y; 670 ix.item = i; 671 ix.pos = 0; 672 ix.line = tktitemline(i); 673 p = tktitempos(tk, &ix); 674 bd = w->sub->borderwidth; 675 mp.x = m.x - (deltasv.x + p.x + w->sub->act.x + bd); 676 mp.y = m.y - (deltasv.y + p.y + w->sub->act.y + bd); 677 ftk = tkinwindow(w->sub, mp, 0); 678 if(ftk != w->focus) { 679 tkdeliver(w->focus, TkLeave, data); 680 tkdeliver(ftk, TkEnter, data); 681 682 w->focus = ftk; 683 } 684 if(ftk != nil) 685 dest = tkdeliver(ftk, event, &m); 686 } 687 else { 688 if ((event & TkLeave) && (w->focus != w->sub)) { 689 tkdeliver(w->focus, TkLeave, data); 690 w->focus = nil; 691 event &= ~TkLeave; 692 } 693 if (event) 694 tkdeliver(w->sub, event, data); 695 } 696 if(Dx(w->sub->dirty) > 0) { 697 l = tktitemline(i); 698 tktfixgeom(tk, tktprevwrapline(tk, l), l, 0); 699 } 700 if(event & TkKey) 701 return dest; 702 } 703 } 704 705 if(tagit != 0) { 706 for(t = tkt->tags; t != nil; t = t->next) { 707 if(t->binds != nil && tkttagset(tagit, t->id)) { 708 if(tksubdeliver(tk, t->binds, event, data, 0) == TkDbreak) { 709 return dest; 710 } 711 } 712 } 713 } 714 } 715 return dest; 716 } 717 718 Tk* 719 tktinwindow(Tk *tk, Point *p) 720 { 721 TkTindex ix; 722 Point q; 723 Tk *sub; 724 725 tktxyind(tk, p->x, p->y, &ix); 726 if (ix.item == nil || ix.item->kind != TkTwin || ix.item->iwin->sub == nil) 727 return tk; 728 sub = ix.item->iwin->sub; 729 q = tktitempos(tk, &ix); 730 p->x -= q.x + sub->borderwidth + sub->act.x; 731 p->y -= q.y + sub->borderwidth + sub->act.y; 732 return sub; 733 } 734 735 Tk* 736 tktextevent(Tk *tk, int event, void *data) 737 { 738 char *e; 739 TkMouse m, vm; 740 TkTitem *f, *tagit; 741 TkText *tkt; 742 TkTindex ix; 743 Tk *dest; 744 Point deltasv; 745 746 tkt = TKobj(TkText, tk); 747 deltasv = tkposn(tk); 748 deltasv.x += tk->borderwidth + tk->ipad.x/2; 749 deltasv.y += tk->borderwidth + tk->ipad.y/2; 750 751 dest = nil; 752 if(event == TkLeave && tkt->mouse != nil) { 753 vm.x = 0; 754 vm.y = 0; 755 tktdeliver(tk, tkt->mouse, tkt->mouse, TkLeave, data, deltasv); 756 tkt->mouse = nil; 757 } 758 else if((event & TkKey) == 0 && (event & TkEmouse)) { 759 /* m in S space, tm in V space */ 760 m = *(TkMouse*)data; 761 vm = m; 762 vm.x -= deltasv.x; 763 vm.y -= deltasv.y; 764 if((event & TkMotion) == 0 || m.b == 0) { 765 tkt->current.x = vm.x; 766 tkt->current.y = vm.y; 767 } 768 tktxyind(tk, vm.x, vm.y, &ix); 769 f = ix.item; 770 if(tkt->mouse != f) { 771 tagit = nil; 772 if(tkt->mouse != nil) { 773 if(tktanytags(tkt->mouse)) { 774 e = tktnewitem(TkTascii, tkt->mouse->tagextra, &tagit); 775 if(e != nil) 776 return dest; /* XXX propagate error? */ 777 tkttagcomb(tagit, tkt->mouse, 1); 778 tkttagcomb(tagit, f, -1); 779 } 780 tktdeliver(tk, tkt->mouse, tagit, TkLeave, data, deltasv); 781 if(tagit) 782 free(tagit); 783 tagit = nil; 784 } 785 if(tktanytags(f)) { 786 e = tktnewitem(TkTascii, f->tagextra, &tagit); 787 if(e != nil) 788 return dest; /* XXX propagate error? */ 789 tkttagcomb(tagit, f, 1); 790 if(tkt->mouse) 791 tkttagcomb(tagit, tkt->mouse, -1); 792 } 793 tktdeliver(tk, f, tagit, TkEnter, data, deltasv); 794 tkt->mouse = f; 795 if(tagit) 796 free(tagit); 797 } 798 if(tkt->mouse != nil) 799 dest = tktdeliver(tk, tkt->mouse, tkt->mouse, event, &m, deltasv); 800 } 801 else if(event == TkFocusin) 802 tktextcursor(tk, " insert", (char **) nil); 803 /* pass all "real" events on to parent text widget - DBK */ 804 tksubdeliver(tk, tk->binds, event, data, 0); 805 return dest; 806 } 807 808 /* Debugging */ 809 void 810 tktprintitem(TkTitem *i) 811 { 812 int j; 813 814 print("%p:", i); 815 switch(i->kind){ 816 case TkTascii: 817 print("\"%s\"", i->istring); 818 break; 819 case TkTrune: 820 print("<rune:%s>", i->istring); 821 break; 822 case TkTnewline: 823 print("<nl:%p>", i->iline); 824 break; 825 case TkTcontline: 826 print("<cont:%p>", i->iline); 827 break; 828 case TkTtab: 829 print("<tab>"); 830 break; 831 case TkTmark: 832 print("<mk:%s>", i->imark->name); 833 break; 834 case TkTwin: 835 if (i->iwin->sub->name != nil) 836 print("<win:%s>", i->iwin->sub? i->iwin->sub->name->name : "<null>"); 837 } 838 print("[%d]", i->width); 839 if(i->tags !=0 || i->tagextra !=0) { 840 print("{%lux", i->tags[0]); 841 for(j=0; j < i->tagextra; j++) 842 print(" %lux", i->tags[j+1]); 843 print("}"); 844 } 845 print(" "); 846 } 847 848 void 849 tktprintline(TkTline *l) 850 { 851 TkTitem *i; 852 853 print("line %p: orig=(%d,%d), w=%d, h=%d, a=%d, f=%x\n\t", 854 l, l->orig.x, l->orig.y, l->width, l->height, l->ascent, l->flags); 855 for(i = l->items; i != nil; i = i->next) 856 tktprintitem(i); 857 print("\n"); 858 } 859 860 void 861 tktprintindex(TkTindex *ix) 862 { 863 print("line=%p,item=%p,pos=%d\n", ix->line, ix->item, ix->pos); 864 } 865 866 void 867 tktprinttext(TkText *tkt) 868 { 869 TkTline *l; 870 TkTtaginfo *ti; 871 TkTmarkinfo *mi; 872 873 for(ti=tkt->tags; ti != nil; ti=ti->next) 874 print("%s{%d} ", ti->name, ti->id); 875 print("\n"); 876 for(mi = tkt->marks; mi != nil; mi=mi->next) 877 print("%s{%p} ", mi->name? mi->name : "nil", mi->cur); 878 print("\n"); 879 print("selfirst=%p sellast=%p\n", tkt->selfirst, tkt->sellast); 880 881 for(l = &tkt->start; l != nil; l = l->next) 882 tktprintline(l); 883 } 884 885 /* 886 * Check that assumed invariants are true. 887 * 888 * - start line and end line have no items 889 * - all other lines have at least one item 890 * - start line leads to end line via next pointers 891 * - prev pointers point to previous lines 892 * - each line ends in either a TkTnewline or a TkTcontline 893 * whose iline pointer points to the line itself 894 * - TkTcontline and TkTmark items have no tags 895 * (this is so they don't get realloc'd because of tag combination) 896 * - all cur fields of marks point to nil or a TkTmark 897 * - selfirst and sellast correctly define select region 898 * - nlines counts the number of lines 899 */ 900 void 901 tktcheck(TkText *tkt, char *fun) 902 { 903 int nl, insel, selfound; 904 TkTline *l; 905 TkTitem *i; 906 TkTmarkinfo *mi; 907 TkTindex ix; 908 char *prob; 909 910 prob = nil; 911 nl = 0; 912 913 if(tkt->start.items != nil || tkt->end.items != nil) 914 prob = "start/end has items"; 915 for(l = tkt->start.next; l != &tkt->end; l = l->next) { 916 if(l->prev->next != l) { 917 prob = "prev mismatch"; 918 break; 919 } 920 if(l->next->prev != l) { 921 prob = "next mismatch"; 922 break; 923 } 924 i = l->items; 925 if(i == nil) { 926 prob = "empty line"; 927 break; 928 } 929 while(i->next != nil) { 930 if(i->kind == TkTnewline || i->kind == TkTcontline) { 931 prob = "premature end of line"; 932 break; 933 } 934 if(i->kind == TkTmark && (i->tags[0] != 0 || i->tagextra != 0)) { 935 prob = "mark has tags"; 936 break; 937 } 938 i = i->next; 939 } 940 if(i->kind == TkTnewline) 941 nl++; 942 if(!(i->kind == TkTnewline || i->kind == TkTcontline)) { 943 prob = "bad end of line"; 944 break; 945 } 946 if(i->kind == TkTcontline && (i->tags[0] != 0 || i->tagextra != 0)) { 947 prob = "contline has tags"; 948 break; 949 } 950 if(i->iline != l) { 951 prob = "bad end-of-line pointer"; 952 break; 953 } 954 } 955 for(mi = tkt->marks; mi != nil; mi=mi->next) { 956 if(mi->cur != nil) { 957 tktstartind(tkt, &ix); 958 do { 959 if(ix.item->kind == TkTmark && ix.item == mi->cur) 960 goto foundmark; 961 } while(tktadjustind(tkt, TkTbyitem, &ix)); 962 prob = "bad mark cur"; 963 break; 964 foundmark: ; 965 } 966 } 967 insel = 0; 968 selfound = 0; 969 tktstartind(tkt, &ix); 970 do { 971 i = ix.item; 972 if(i == tkt->selfirst) { 973 if(i->kind == TkTmark || i->kind == TkTcontline) { 974 prob = "selfirst not on character"; 975 break; 976 } 977 if(i == tkt->sellast) { 978 prob = "selfirst==sellast, not nil"; 979 break; 980 } 981 insel = 1; 982 selfound = 1; 983 } 984 if(i == tkt->sellast) { 985 if(i->kind == TkTmark || i->kind == TkTcontline) { 986 prob = "sellast not on character"; 987 break; 988 } 989 insel = 0; 990 } 991 if(i->kind != TkTmark && i->kind != TkTcontline) { 992 if(i->tags[0] & (1<<TkTselid)) { 993 if(!insel) { 994 prob = "sel set outside selfirst..sellast"; 995 break; 996 } 997 } 998 else { 999 if(insel) { 1000 prob = "sel not set inside selfirst..sellast"; 1001 break; 1002 } 1003 } 1004 } 1005 } while(tktadjustind(tkt, TkTbyitem, &ix)); 1006 if(tkt->selfirst != nil && !selfound) 1007 prob = "selfirst not found"; 1008 1009 if(prob != nil) { 1010 print("tktcheck problem: %s: %s\n", fun, prob); 1011 tktprinttext(tkt); 1012 abort(); 1013 } 1014 } 1015 1016 int 1017 tktutfpos(char *s, int pos) 1018 { 1019 char *s1; 1020 int c; 1021 Rune r; 1022 1023 for (s1 = s; pos > 0; pos--) { 1024 c = *(uchar *)s1; 1025 if (c < Runeself) { 1026 if (c == '\0') 1027 break; 1028 s1++; 1029 } 1030 else 1031 s1 += chartorune(&r, s1); 1032 } 1033 return s1 - s; 1034 } 1035 1036 /* 1037 struct timerec { 1038 char *name; 1039 ulong ms; 1040 }; 1041 1042 static struct timerec tt[100]; 1043 static int ntt = 1; 1044 1045 int 1046 tktalloctime(char *name) 1047 { 1048 if(ntt >= 100) 1049 abort(); 1050 tt[ntt].name = strdup(name); 1051 tt[ntt].ms = 0; 1052 return ntt++; 1053 } 1054 1055 void 1056 tktstarttime(int ind) 1057 { 1058 return; 1059 tt[ind].ms -= osmillisec(); 1060 } 1061 1062 void 1063 tktendtime(int ind) 1064 { 1065 return; 1066 tt[ind].ms += osmillisec(); 1067 } 1068 1069 void 1070 tktdumptime(void) 1071 { 1072 int i; 1073 1074 for(i = 1; i < ntt; i++) 1075 print("%s: %d\n", tt[i].name, tt[i].ms); 1076 } 1077 */ 1078