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 static char* tkttagadd(Tk*, char*, char**); 12 static char* tkttagbind(Tk*, char*, char**); 13 static char* tkttagcget(Tk*, char*, char**); 14 static char* tkttagconfigure(Tk*, char*, char**); 15 static char* tkttagdelete(Tk*, char*, char**); 16 static char* tkttaglower(Tk*, char*, char**); 17 static char* tkttagnames(Tk*, char*, char**); 18 static char* tkttagnextrange(Tk*, char*, char**); 19 static char* tkttagprevrange(Tk*, char*, char**); 20 static char* tkttagraise(Tk*, char*, char**); 21 static char* tkttagranges(Tk*, char*, char**); 22 static char* tkttagremove(Tk*, char*, char**); 23 24 #define O(t, e) ((long)(&((t*)0)->e)) 25 26 #define TKTEO (O(TkTtaginfo, env)) 27 static 28 TkOption tagopts[] = 29 { 30 "borderwidth", 31 OPTnndist, O(TkTtaginfo, opts[TkTborderwidth]), nil, 32 "justify", 33 OPTstab, O(TkTtaginfo, opts[TkTjustify]), tkjustify, 34 "lineheight", 35 OPTnndist, O(TkTtaginfo, opts[TkTlineheight]), IAUX(TKTEO), 36 "lmargin1", 37 OPTdist, O(TkTtaginfo, opts[TkTlmargin1]), IAUX(TKTEO), 38 "lmargin2", 39 OPTdist, O(TkTtaginfo, opts[TkTlmargin2]), IAUX(TKTEO), 40 "lmargin3", 41 OPTdist, O(TkTtaginfo, opts[TkTlmargin3]), IAUX(TKTEO), 42 "rmargin", 43 OPTdist, O(TkTtaginfo, opts[TkTrmargin]), IAUX(TKTEO), 44 "spacing1", 45 OPTnndist, O(TkTtaginfo, opts[TkTspacing1]), IAUX(TKTEO), 46 "spacing2", 47 OPTnndist, O(TkTtaginfo, opts[TkTspacing2]), IAUX(TKTEO), 48 "spacing3", 49 OPTnndist, O(TkTtaginfo, opts[TkTspacing3]), IAUX(TKTEO), 50 "offset", 51 OPTdist, O(TkTtaginfo, opts[TkToffset]), IAUX(TKTEO), 52 "underline", 53 OPTstab, O(TkTtaginfo, opts[TkTunderline]), tkbool, 54 "overstrike", 55 OPTstab, O(TkTtaginfo, opts[TkToverstrike]), tkbool, 56 "relief", 57 OPTstab, O(TkTtaginfo, opts[TkTrelief]), tkrelief, 58 "tabs", 59 OPTtabs, O(TkTtaginfo, tabs), IAUX(TKTEO), 60 "wrap", 61 OPTstab, O(TkTtaginfo, opts[TkTwrap]), tkwrap, 62 nil, 63 }; 64 65 static 66 TkOption tagenvopts[] = 67 { 68 "foreground", OPTcolr, O(TkTtaginfo, env), IAUX(TkCforegnd), 69 "background", OPTcolr, O(TkTtaginfo, env), IAUX(TkCbackgnd), 70 "fg", OPTcolr, O(TkTtaginfo, env), IAUX(TkCforegnd), 71 "bg", OPTcolr, O(TkTtaginfo, env), IAUX(TkCbackgnd), 72 "font", OPTfont, O(TkTtaginfo, env), nil, 73 nil 74 }; 75 76 TkCmdtab 77 tkttagcmd[] = 78 { 79 "add", tkttagadd, 80 "bind", tkttagbind, 81 "cget", tkttagcget, 82 "configure", tkttagconfigure, 83 "delete", tkttagdelete, 84 "lower", tkttaglower, 85 "names", tkttagnames, 86 "nextrange", tkttagnextrange, 87 "prevrange", tkttagprevrange, 88 "raise", tkttagraise, 89 "ranges", tkttagranges, 90 "remove", tkttagremove, 91 nil 92 }; 93 94 int 95 tktanytags(TkTitem *it) 96 { 97 int i; 98 99 if(it->tagextra == 0) 100 return (it->tags[0] != 0); 101 for(i = 0; i <= it->tagextra; i++) 102 if(it->tags[i] != 0) 103 return 1; 104 return 0; 105 } 106 107 int 108 tktsametags(TkTitem *i1, TkTitem *i2) 109 { 110 int i, j; 111 112 for(i = 0; i <= i1->tagextra && i <= i2->tagextra; i++) 113 if(i1->tags[i] != i2->tags[i]) 114 return 0; 115 for(j = i; j <= i1->tagextra; j++) 116 if(i1->tags[j] != 0) 117 return 0; 118 for(j = i; j <= i2->tagextra; j++) 119 if(i2->tags[j] != 0) 120 return 0; 121 return 1; 122 } 123 124 int 125 tkttagset(TkTitem *it, int id) 126 { 127 int i; 128 129 if(it->tagextra == 0 && it->tags[0] == 0) 130 return 0; 131 for(i = 0; i <= it->tagextra; i++) { 132 if(id < 32) 133 return ((it->tags[i] & (1<<id)) != 0); 134 id -= 32; 135 } 136 return 0; 137 } 138 139 char * 140 tkttagname(TkText *tkt, int id) 141 { 142 TkTtaginfo *t; 143 144 for(t = tkt->tags; t != nil; t = t->next) { 145 if(t->id == id) 146 return t->name; 147 } 148 return ""; 149 } 150 151 /* return 1 if this actually changes the value */ 152 int 153 tkttagbit(TkTitem *it, int id, int val) 154 { 155 int i, changed; 156 ulong z, b; 157 158 changed = 0; 159 for(i = 0; i <= it->tagextra; i++) { 160 if(id < 32) { 161 b = (1<<id); 162 z = it->tags[i]; 163 if(val == 0) { 164 if(z & b) { 165 changed = 1; 166 it->tags[i] = z & (~b); 167 } 168 } 169 else { 170 if((z & b) == 0) { 171 changed = 1; 172 it->tags[i] = z | b; 173 } 174 } 175 break; 176 } 177 id -= 32; 178 } 179 return changed; 180 } 181 182 void 183 tkttagcomb(TkTitem *i1, TkTitem *i2, int add) 184 { 185 int i; 186 187 for(i = 0; i <= i1->tagextra && i <= i2->tagextra; i++) { 188 if(add == 1) 189 i1->tags[i] |= i2->tags[i]; 190 else if(add == 0) 191 /* intersect */ 192 i1->tags[i] &= i2->tags[i]; 193 else 194 /* subtract */ 195 i1->tags[i] &= ~i2->tags[i]; 196 } 197 } 198 199 char* 200 tktaddtaginfo(Tk *tk, char *name, TkTtaginfo **ret) 201 { 202 int i, *ntagp; 203 TkTtaginfo *ti; 204 TkText *tkt, *tktshare; 205 206 tkt = TKobj(TkText, tk); 207 ti = malloc(sizeof(TkTtaginfo)); 208 if(ti == nil) 209 return TkNomem; 210 211 ntagp = &tkt->nexttag; 212 if(tkt->tagshare != nil) { 213 tktshare = TKobj(TkText, tkt->tagshare); 214 ntagp = &tktshare->nexttag; 215 } 216 ti->id = *ntagp; 217 ti->name = strdup(name); 218 if(ti->name == nil) { 219 free(ti); 220 return TkNomem; 221 } 222 ti->env = tknewenv(tk->env->top); 223 if(ti->env == nil) { 224 free(ti->name); 225 free(ti); 226 return TkNomem; 227 } 228 229 ti->tabs = nil; 230 for(i = 0; i < TkTnumopts; i++) 231 ti->opts[i] = TkTunset; 232 ti->next = tkt->tags; 233 tkt->tags = ti; 234 235 (*ntagp)++; 236 if(tkt->tagshare) 237 tkt->nexttag = *ntagp; 238 239 *ret = ti; 240 return nil; 241 } 242 243 TkTtaginfo * 244 tktfindtag(TkTtaginfo *t, char *name) 245 { 246 while(t != nil) { 247 if(strcmp(t->name, name) == 0) 248 return t; 249 t = t->next; 250 } 251 return nil; 252 } 253 254 void 255 tktfreetags(TkTtaginfo *t) 256 { 257 TkTtaginfo *n; 258 259 while(t != nil) { 260 n = t->next; 261 free(t->name); 262 tktfreetabs(t->tabs); 263 tkputenv(t->env); 264 tkfreebind(t->binds); 265 free(t); 266 t = n; 267 } 268 } 269 270 int 271 tkttagind(Tk *tk, char *name, int first, TkTindex *ans) 272 { 273 int id; 274 TkTtaginfo *t; 275 TkText *tkt; 276 277 tkt = TKobj(TkText, tk); 278 279 if(strcmp(name, "sel") == 0) { 280 if(tkt->selfirst == nil) 281 return 0; 282 if(first) 283 tktitemind(tkt->selfirst, ans); 284 else 285 tktitemind(tkt->sellast, ans); 286 return 1; 287 } 288 289 t = tktfindtag(tkt->tags, name); 290 if(t == nil) 291 return 0; 292 id = t->id; 293 294 if(first) { 295 tktstartind(tkt, ans); 296 while(!tkttagset(ans->item, id)) 297 if(!tktadjustind(tkt, TkTbyitem, ans)) 298 return 0; 299 } 300 else { 301 tktendind(tkt, ans); 302 while(!tkttagset(ans->item, id)) 303 if(!tktadjustind(tkt, TkTbyitemback, ans)) 304 return 0; 305 tktadjustind(tkt, TkTbyitem, ans); 306 } 307 308 return 1; 309 } 310 311 /* 312 * Fill in opts and e, based on info from tags set in it, 313 * using tags order for priority. 314 * If dflt != 0, options not set are filled from tk, 315 * otherwise iInteger options not set by any tag are left 'TkTunset' 316 * and environment values not set are left nil. 317 */ 318 void 319 tkttagopts(Tk *tk, TkTitem *it, int *opts, TkEnv *e, TkTtabstop **tb, int dflt) 320 { 321 int i; 322 int colset; 323 TkEnv *te; 324 TkTtaginfo *tags; 325 TkText *tkt = TKobj(TkText, tk); 326 327 if (tb != nil) 328 *tb = tkt->tabs; 329 330 tags = tkt->tags; 331 332 if(opts != nil) 333 for(i = 0; i < TkTnumopts; i++) 334 opts[i] = TkTunset; 335 336 memset(e, 0, sizeof(TkEnv)); 337 e->top = tk->env->top; 338 colset = 0; 339 while(tags != nil) { 340 if(tkttagset(it, tags->id)) { 341 if(opts != nil) { 342 for(i = 0; i < TkTnumopts; i++) { 343 if(opts[i] == TkTunset && tags->opts[i] != TkTunset) 344 opts[i] = tags->opts[i]; 345 } 346 } 347 348 te = tags->env; 349 for(i = 0; i < TkNcolor; i++) 350 if(!(colset & (1<<i)) && te->set & (1<<i)) { 351 e->colors[i] = te->colors[i]; 352 colset |= 1<<i; 353 } 354 355 if(e->font == nil && te->font != nil) 356 e->font = te->font; 357 358 if (tb != nil && tags->tabs != nil) 359 *tb = tags->tabs; 360 } 361 tags = tags->next; 362 } 363 e->set |= colset; 364 if(dflt) { 365 if(opts != nil) { 366 for(i = 0; i < TkTnumopts; i++) 367 if(opts[i] == TkTunset) 368 opts[i] = tkt->opts[i]; 369 } 370 te = tk->env; 371 for(i = 0; i < TkNcolor; i++) 372 if(!(e->set & (1<<i))) { 373 e->colors[i] = te->colors[i]; 374 e->set |= 1<<i; 375 } 376 if(e->font == nil) 377 e->font = te->font; 378 } 379 } 380 381 char* 382 tkttagparse(Tk *tk, char **parg, TkTtaginfo **ret) 383 { 384 char *e, *buf; 385 TkText *tkt = TKobj(TkText, tk); 386 387 buf = mallocz(Tkmaxitem, 0); 388 if(buf == nil) 389 return TkNomem; 390 *parg = tkword(tk->env->top, *parg, buf, buf+Tkmaxitem, nil); 391 if(*buf == '\0') { 392 free(buf); 393 return TkOparg; 394 } 395 if(buf[0] >= '0' && buf[0] <= '9'){ 396 free(buf); 397 return TkBadtg; 398 } 399 400 *ret = tktfindtag(tkt->tags, buf); 401 if(*ret == nil) { 402 e = tktaddtaginfo(tk, buf, ret); 403 if(e != nil) { 404 free(buf); 405 return e; 406 } 407 } 408 free(buf); 409 410 return nil; 411 } 412 413 int 414 tkttagnrange(TkText *tkt, int tid, TkTindex *i1, TkTindex *i2, 415 TkTindex *istart, TkTindex *iend) 416 { 417 int found; 418 419 found = 0; 420 while(i1->line != &tkt->end) { 421 if(i1->item == i2->item && i2->pos == 0) 422 break; 423 if(tkttagset(i1->item, tid)) { 424 if(!found) { 425 found = 1; 426 *istart = *i1; 427 } 428 if(i1->item == i2->item) { 429 /* i2->pos > 0 */ 430 *iend = *i2; 431 return 1; 432 } 433 } 434 else 435 if(i1->item == i2->item || (found && i1->item->kind != TkTmark && i1->item->kind != TkTcontline)) 436 break; 437 tktadjustind(tkt, TkTbyitem, i1); 438 } 439 if(found) 440 *iend = *i1; 441 442 return found; 443 } 444 445 static int 446 tkttagprange(TkText *tkt, int tid, TkTindex *i1, TkTindex *i2, 447 TkTindex *istart, TkTindex *iend) 448 { 449 int found; 450 451 found = 0; 452 while(i1->line != &tkt->start && i1->item != i2->item) { 453 tktadjustind(tkt, TkTbyitemback, i1); 454 if(tkttagset(i1->item, tid)) { 455 if(!found) { 456 found = 1; 457 *iend = *i1; 458 } 459 } 460 else 461 if(found && i1->item->kind != TkTmark && i1->item->kind != TkTcontline) 462 break; 463 } 464 if(found) { 465 tktadjustind(tkt, TkTbyitem, i1); 466 *istart = *i1; 467 if(i1->item == i2->item) 468 istart->pos = i2->pos; 469 } 470 471 return found; 472 } 473 474 /* XXX - Tad: potential memory leak on memory allocation failure */ 475 char * 476 tkttagchange(Tk *tk, int tid, TkTindex *i1, TkTindex *i2, int add) 477 { 478 char *e; 479 int samei, nextra, j, changed; 480 TkTline *lmin, *lmax; 481 TkTindex ixprev; 482 TkTitem *nit; 483 TkText *tkt = TKobj(TkText, tk); 484 485 if(!tktindbefore(i1, i2)) 486 return nil; 487 488 nextra = tid/32; 489 lmin = nil; 490 lmax = nil; 491 tktadjustind(tkt, TkTbycharstart, i1); 492 tktadjustind(tkt, TkTbycharstart, i2); 493 samei = (i1->item == i2->item); 494 if(i2->pos != 0) { 495 e = tktsplititem(i2); 496 if(e != nil) 497 return e; 498 if(samei) { 499 /* split means i1 should now point to previous item */ 500 ixprev = *i2; 501 tktadjustind(tkt, TkTbyitemback, &ixprev); 502 i1->item = ixprev.item; 503 } 504 } 505 if(i1->pos != 0) { 506 e = tktsplititem(i1); 507 if(e != nil) 508 return e; 509 } 510 /* now i1 and i2 both point to beginning of non-mark/contline items */ 511 if(tid == TkTselid) { 512 /* 513 * Cache location of selection. 514 * Note: there can be only one selection range in widget 515 */ 516 if(add) { 517 if(tkt->selfirst != nil) 518 return TkBadsl; 519 tkt->selfirst = i1->item; 520 tkt->sellast = i2->item; 521 } 522 else { 523 tkt->selfirst = nil; 524 tkt->sellast = nil; 525 } 526 } 527 while(i1->item != i2->item) { 528 if(i1->item->kind != TkTmark && i1->item->kind != TkTcontline) { 529 if(tid >= 32 && i1->item->tagextra < nextra) { 530 nit = realloc(i1->item, sizeof(TkTitem) + nextra * sizeof(long)); 531 if(nit == nil) 532 return TkNomem; 533 for(j = nit->tagextra+1; j <= nextra; j++) 534 nit->tags[j] = 0; 535 nit->tagextra = nextra; 536 if(i1->line->items == i1->item) 537 i1->line->items = nit; 538 else { 539 ixprev = *i1; 540 tktadjustind(tkt, TkTbyitemback, &ixprev); 541 ixprev.item->next = nit; 542 } 543 /* check nit against cached items */ 544 if(tkt->selfirst == i1->item) 545 tkt->selfirst = nit; 546 if(tkt->sellast == i1->item) 547 tkt->sellast = nit; 548 i1->item = nit; 549 } 550 changed = tkttagbit(i1->item, tid, add); 551 if(lmin == nil) { 552 if(changed) { 553 lmin = i1->line; 554 lmax = lmin; 555 } 556 } 557 else { 558 if(changed) 559 lmax = i1->line; 560 } 561 } 562 if(!tktadjustind(tkt, TkTbyitem, i1)) 563 break; 564 } 565 if(lmin != nil) { 566 tktfixgeom(tk, tktprevwrapline(tk, lmin), lmax, 0); 567 tktextsize(tk, 1); 568 } 569 return nil; 570 } 571 572 static char* 573 tkttagaddrem(Tk *tk, char *arg, int add) 574 { 575 char *e; 576 TkText *tkt; 577 TkTtaginfo *ti; 578 TkTindex ix1, ix2; 579 580 tkt = TKobj(TkText, tk); 581 582 e = tkttagparse(tk, &arg, &ti); 583 if(e != nil) 584 return e; 585 586 while(*arg != '\0') { 587 e = tktindparse(tk, &arg, &ix1); 588 if(e != nil) 589 return e; 590 if(*arg != '\0') { 591 e = tktindparse(tk, &arg, &ix2); 592 if(e != nil) 593 return e; 594 } 595 else { 596 ix2 = ix1; 597 tktadjustind(tkt, TkTbychar, &ix2); 598 } 599 if(!tktindbefore(&ix1, &ix2)) 600 continue; 601 602 e = tkttagchange(tk, ti->id, &ix1, &ix2, add); 603 if(e != nil) 604 return e; 605 } 606 607 return nil; 608 } 609 610 611 /* Text Tag Command (+ means implemented) 612 +add 613 +bind 614 +cget 615 +configure 616 +delete 617 +lower 618 +names 619 +nextrange 620 +prevrange 621 +raise 622 +ranges 623 +remove 624 */ 625 626 static char* 627 tkttagadd(Tk *tk, char *arg, char **val) 628 { 629 USED(val); 630 631 return tkttagaddrem(tk, arg, 1); 632 } 633 634 static char* 635 tkttagbind(Tk *tk, char *arg, char **val) 636 { 637 char *e; 638 Rune r; 639 TkTtaginfo *ti; 640 TkAction *a; 641 int event, mode; 642 char *cmd, buf[Tkmaxitem]; 643 644 645 e = tkttagparse(tk, &arg, &ti); 646 if(e != nil) 647 return e; 648 649 arg = tkskip(arg, " \t"); 650 if (arg[0] == '\0') 651 return TkBadsq; 652 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 653 if(buf[0] == '<') { 654 event = tkseqparse(buf+1); 655 if(event == -1) 656 return TkBadsq; 657 } 658 else { 659 chartorune(&r, buf); 660 event = TkKey | r; 661 } 662 if(event == 0) 663 return TkBadsq; 664 665 arg = tkskip(arg, " \t"); 666 if(*arg == '\0') { 667 for(a = ti->binds; a; a = a->link) 668 if(event == a->event) 669 return tkvalue(val, "%s", a->arg); 670 return nil; 671 } 672 673 mode = TkArepl; 674 if(*arg == '+') { 675 mode = TkAadd; 676 arg++; 677 } 678 else if(*arg == '-'){ 679 mode = TkAsub; 680 arg++; 681 } 682 683 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 684 cmd = strdup(buf); 685 if(cmd == nil) 686 return TkNomem; 687 return tkaction(&ti->binds, event, TkDynamic, cmd, mode); 688 } 689 690 static char* 691 tkttagcget(Tk *tk, char *arg, char **val) 692 { 693 char *e; 694 TkTtaginfo *ti; 695 TkOptab tko[3]; 696 697 e = tkttagparse(tk, &arg, &ti); 698 if(e != nil) 699 return e; 700 701 tko[0].ptr = ti; 702 tko[0].optab = tagopts; 703 tko[1].ptr = ti; 704 tko[1].optab = tagenvopts; 705 tko[2].ptr = nil; 706 707 return tkgencget(tko, arg, val, tk->env->top); 708 } 709 710 static char* 711 tkttagconfigure(Tk *tk, char *arg, char **val) 712 { 713 char *e; 714 TkOptab tko[3]; 715 TkTtaginfo *ti; 716 TkTindex ix; 717 TkText *tkt = TKobj(TkText, tk); 718 719 USED(val); 720 721 e = tkttagparse(tk, &arg, &ti); 722 if(e != nil) 723 return e; 724 725 tko[0].ptr = ti; 726 tko[0].optab = tagopts; 727 tko[1].ptr = ti; 728 tko[1].optab = tagenvopts; 729 tko[2].ptr = nil; 730 731 e = tkparse(tk->env->top, arg, tko, nil); 732 if(e != nil) 733 return e; 734 735 if(tkttagind(tk, ti->name, 1, &ix)) { 736 tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0); 737 tktextsize(tk, 1); 738 } 739 740 return nil; 741 } 742 743 static void 744 tktunlinktag(TkText *tkt, TkTtaginfo *t) 745 { 746 TkTtaginfo *f, **l; 747 748 l = &tkt->tags; 749 for(f = *l; f != nil; f = f->next) { 750 if(f == t) { 751 *l = t->next; 752 return; 753 } 754 l = &f->next; 755 } 756 } 757 758 static char* 759 tkttagdelete(Tk *tk, char *arg, char **val) 760 { 761 TkText *tkt; 762 TkTtaginfo *t; 763 TkTindex ix; 764 char *e; 765 int found; 766 767 USED(val); 768 769 tkt = TKobj(TkText, tk); 770 771 e = tkttagparse(tk, &arg, &t); 772 if(e != nil) 773 return e; 774 775 found = 0; 776 while(t != nil) { 777 if(t->id == TkTselid) 778 return TkBadvl; 779 780 while(tkttagind(tk, t->name, 1, &ix)) { 781 found = 1; 782 tkttagbit(ix.item, t->id, 0); 783 } 784 785 tktunlinktag(tkt, t); 786 t->next = nil; 787 tktfreetags(t); 788 789 if(*arg != '\0') { 790 e = tkttagparse(tk, &arg, &t); 791 if(e != nil) 792 return e; 793 } 794 else 795 t = nil; 796 } 797 if (found) { 798 tktfixgeom(tk, &tkt->start, tkt->end.prev, 0); 799 tktextsize(tk, 1); 800 } 801 802 return nil; 803 } 804 805 static char* 806 tkttaglower(Tk *tk, char *arg, char **val) 807 { 808 TkText *tkt; 809 TkTindex ix; 810 TkTtaginfo *t, *tbelow, *f, **l; 811 char *e; 812 813 USED(val); 814 815 tkt = TKobj(TkText, tk); 816 817 e = tkttagparse(tk, &arg, &t); 818 if(e != nil) 819 return e; 820 821 if(*arg != '\0') { 822 e = tkttagparse(tk, &arg, &tbelow); 823 if(e != nil) 824 return e; 825 } 826 else 827 tbelow = nil; 828 829 tktunlinktag(tkt, t); 830 831 if(tbelow != nil) { 832 t->next = tbelow->next; 833 tbelow->next = t; 834 } 835 else { 836 l = &tkt->tags; 837 for(f = *l; f != nil; f = f->next) 838 l = &f->next; 839 *l = t; 840 t->next = nil; 841 } 842 if(tkttagind(tk, t->name, 1, &ix)) { 843 tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0); 844 tktextsize(tk, 1); 845 } 846 847 return nil; 848 } 849 850 851 static char* 852 tkttagnames(Tk *tk, char *arg, char **val) 853 { 854 char *e, *r, *fmt; 855 TkTtaginfo *t; 856 TkTindex i; 857 TkText *tkt = TKobj(TkText, tk); 858 TkTitem *tagit; 859 860 if(*arg != '\0') { 861 e = tktindparse(tk, &arg, &i); 862 if(e != nil) 863 return e; 864 /* make sure we're actually on a character */ 865 tktadjustind(tkt, TkTbycharstart, &i); 866 tagit = i.item; 867 } 868 else 869 tagit = nil; 870 871 /* generate in order highest-to-lowest priority (contrary to spec) */ 872 fmt = "%s"; 873 for(t = tkt->tags; t != nil; t = t->next) { 874 if(tagit == nil || tkttagset(tagit, t->id)) { 875 r = tkvalue(val, fmt, t->name); 876 if(r != nil) 877 return r; 878 fmt = " %s"; 879 } 880 } 881 return nil; 882 } 883 884 static char* 885 tkttagnextrange(Tk *tk, char *arg, char **val) 886 { 887 char *e; 888 TkTtaginfo *t; 889 TkTindex i1, i2, istart, iend; 890 TkText *tkt = TKobj(TkText, tk); 891 892 e = tkttagparse(tk, &arg, &t); 893 if(e != nil) 894 return e; 895 e = tktindparse(tk, &arg, &i1); 896 if(e != nil) 897 return e; 898 if(*arg != '\0') { 899 e = tktindparse(tk, &arg, &i2); 900 if(e != nil) 901 return e; 902 } 903 else 904 tktendind(tkt, &i2); 905 906 if(tkttagnrange(tkt, t->id, &i1, &i2, &istart, &iend)) 907 return tkvalue(val, "%d.%d %d.%d", 908 tktlinenum(tkt, &istart), tktlinepos(tkt, &istart), 909 tktlinenum(tkt, &iend), tktlinepos(tkt, &iend)); 910 911 return nil; 912 } 913 914 static char* 915 tkttagprevrange(Tk *tk, char *arg, char **val) 916 { 917 char *e; 918 TkTtaginfo *t; 919 TkTindex i1, i2, istart, iend; 920 TkText *tkt = TKobj(TkText, tk); 921 922 e = tkttagparse(tk, &arg, &t); 923 if(e != nil) 924 return e; 925 e = tktindparse(tk, &arg, &i1); 926 if(e != nil) 927 return e; 928 if(*arg != '\0') { 929 e = tktindparse(tk, &arg, &i2); 930 if(e != nil) 931 return e; 932 } 933 else 934 tktstartind(tkt, &i2); 935 936 if(tkttagprange(tkt, t->id, &i1, &i2, &istart, &iend)) 937 return tkvalue(val, "%d.%d %d.%d", 938 tktlinenum(tkt, &istart), tktlinepos(tkt, &istart), 939 tktlinenum(tkt, &iend), tktlinepos(tkt, &iend)); 940 941 return nil; 942 } 943 944 static char* 945 tkttagraise(Tk *tk, char *arg, char **val) 946 { 947 TkText *tkt; 948 TkTindex ix; 949 TkTtaginfo *t, *tabove, *f, **l; 950 char *e; 951 952 USED(val); 953 954 tkt = TKobj(TkText, tk); 955 956 e = tkttagparse(tk, &arg, &t); 957 if(e != nil) 958 return e; 959 960 if(*arg != '\0') { 961 e = tkttagparse(tk, &arg, &tabove); 962 if(e != nil) 963 return e; 964 } 965 else 966 tabove = nil; 967 968 tktunlinktag(tkt, t); 969 970 if(tabove != nil) { 971 l = &tkt->tags; 972 for(f = *l; f != nil; f = f->next) { 973 if(f == tabove) { 974 *l = t; 975 t->next = tabove; 976 break; 977 } 978 l = &f->next; 979 } 980 } 981 else { 982 t->next = tkt->tags; 983 tkt->tags = t; 984 } 985 986 if(tkttagind(tk, t->name, 1, &ix)) { 987 tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0); 988 tktextsize(tk, 1); 989 } 990 return nil; 991 } 992 993 static char* 994 tkttagranges(Tk *tk, char *arg, char **val) 995 { 996 char *e, *fmt; 997 TkTtaginfo *t; 998 TkTindex i1, i2, istart, iend; 999 TkText *tkt = TKobj(TkText, tk); 1000 1001 e = tkttagparse(tk, &arg, &t); 1002 if(e != nil) 1003 return e; 1004 1005 tktstartind(tkt, &i1); 1006 tktendind(tkt, &i2); 1007 1008 fmt = "%d.%d %d.%d"; 1009 while(tkttagnrange(tkt, t->id, &i1, &i2, &istart, &iend)) { 1010 e = tkvalue(val, fmt, 1011 tktlinenum(tkt, &istart), tktlinepos(tkt, &istart), 1012 tktlinenum(tkt, &iend), tktlinepos(tkt, &iend)); 1013 if(e != nil) 1014 return e; 1015 1016 fmt = " %d.%d %d.%d"; 1017 i1 = iend; 1018 } 1019 1020 return nil; 1021 } 1022 1023 static char* 1024 tkttagremove(Tk *tk, char *arg, char **val) 1025 { 1026 USED(val); 1027 1028 return tkttagaddrem(tk, arg, 0); 1029 } 1030