1 #include "lib9.h" 2 #include "draw.h" 3 #include "tk.h" 4 #include "label.h" 5 6 #define O(t, e) ((long)(&((t*)0)->e)) 7 8 /* Widget Commands (+ means implemented) 9 +cget 10 +configure 11 +invoke 12 +select 13 +deselect 14 +toggle 15 */ 16 17 enum { 18 /* other constants */ 19 InvokePause = 200, /* delay showing button in down state when invoked */ 20 }; 21 22 TkOption tkbutopts[] = 23 { 24 "text", OPTtext, O(TkLabel, text), nil, 25 "label", OPTtext, O(TkLabel, text), nil, 26 "underline", OPTdist, O(TkLabel, ul), nil, 27 "justify", OPTstab, O(TkLabel, justify), tkjustify, 28 "anchor", OPTflag, O(TkLabel, anchor), tkanchor, 29 "command", OPTtext, O(TkLabel, command), nil, 30 "bitmap", OPTbmap, O(TkLabel, bitmap), nil, 31 "image", OPTimag, O(TkLabel, img), nil, 32 nil 33 }; 34 35 TkOption tkcbopts[] = 36 { 37 "variable", OPTtext, O(TkLabel, variable), nil, 38 "indicatoron", OPTstab, O(TkLabel, indicator), tkbool, 39 "onvalue", OPTtext, O(TkLabel, value), nil, 40 "offvalue", OPTtext, O(TkLabel, offvalue), nil, 41 nil, 42 }; 43 44 TkOption tkradopts[] = 45 { 46 "variable", OPTtext, O(TkLabel, variable), nil, 47 "value", OPTtext, O(TkLabel, value), nil, 48 "indicatoron", OPTstab, O(TkLabel, indicator), tkbool, 49 nil, 50 }; 51 52 static 53 TkEbind bb[] = 54 { 55 {TkEnter, "%W configure -state active"}, 56 {TkLeave, "%W configure -state normal"}, 57 {TkButton1P, "%W tkButton1P"}, 58 {TkButton1R, "%W tkButton1R %x %y"}, 59 {TkMotion|TkButton1P, "" }, 60 {TkKey, "%W tkButtonKey 0x%K"}, 61 }; 62 63 static 64 TkEbind cb[] = 65 { 66 {TkEnter, "%W configure -state active"}, 67 {TkLeave, "%W configure -state normal"}, 68 {TkButton1P, "%W invoke"}, 69 {TkMotion|TkButton1P, "" }, 70 {TkKey, "%W tkButtonKey 0x%K"}, 71 }; 72 73 74 static char tkselbut[] = "selectedButton"; 75 76 static char* newbutton(TkTop*, int, char*, char**); 77 static int istransparent(Tk*); 78 static void tkvarchanged(Tk*, char*, char*); 79 80 char* 81 tkbutton(TkTop *t, char *arg, char **ret) 82 { 83 return newbutton(t, TKbutton, arg, ret); 84 } 85 86 char* 87 tkcheckbutton(TkTop *t, char *arg, char **ret) 88 { 89 return newbutton(t, TKcheckbutton, arg, ret); 90 } 91 92 char* 93 tkradiobutton(TkTop *t, char *arg, char **ret) 94 { 95 return newbutton(t, TKradiobutton, arg, ret); 96 } 97 98 static char* 99 newbutton(TkTop *t, int btype, char *arg, char **ret) 100 { 101 Tk *tk; 102 char *e; 103 TkLabel *tkl; 104 TkName *names; 105 TkOptab tko[4]; 106 TkVar *v; 107 108 tk = tkmkbutton(t, btype); 109 if(tk == nil) 110 return TkNomem; 111 112 tkl = TKobj(TkLabel, tk); 113 114 tko[0].ptr = tk; 115 tko[0].optab = tkgeneric; 116 tko[1].ptr = tkl; 117 tko[1].optab = tkbutopts; 118 switch(btype){ 119 case TKcheckbutton: 120 tko[2].ptr = tkl; 121 tko[2].optab = tkcbopts; 122 break; 123 case TKradiobutton: 124 tko[2].ptr = tkl; 125 tko[2].optab = tkradopts; 126 break; 127 default: 128 tk->relief = TKraised; 129 tk->borderwidth = 1; 130 tko[2].ptr = nil; 131 break; 132 } 133 tko[3].ptr = nil; 134 135 names = nil; 136 e = tkparse(t, arg, tko, &names); 137 if(e != nil) { 138 tkfreeobj(tk); 139 return e; 140 } 141 142 tksettransparent(tk, istransparent(tk)); 143 tksizebutton(tk); 144 145 e = tkaddchild(t, tk, &names); 146 tkfreename(names); 147 if(e != nil) { 148 tkfreeobj(tk); 149 return e; 150 } 151 tk->name->link = nil; 152 153 if (btype == TKradiobutton && 154 tkl->variable != nil && 155 strcmp(tkl->variable, tkselbut) == 0 && 156 tkl->value == nil && 157 tk->name != nil) 158 tkl->value = strdup(tk->name->name); 159 160 if (tkl->variable != nil) { 161 v = tkmkvar(t, tkl->variable, 0); 162 if (v == nil){ 163 if(btype == TKcheckbutton){ 164 e = tksetvar(t, tkl->variable, tkl->offvalue ? tkl->offvalue : "0"); 165 if (e != nil) 166 goto err; 167 } 168 } else if(v->type != TkVstring){ 169 e = TkNotvt; 170 goto err; 171 } else 172 tkvarchanged(tk, tkl->variable, v->value); 173 } 174 175 return tkvalue(ret, "%s", tk->name->name); 176 177 err: 178 tkfreeobj(tk); 179 return e; 180 } 181 182 Tk* 183 tkmkbutton(TkTop *t, int btype) 184 { 185 Tk *tk; 186 TkLabel *tkl; 187 char *e; 188 189 tk = tknewobj(t, btype, sizeof(Tk)+sizeof(TkLabel)); 190 if (tk == nil) 191 return nil; 192 193 e = nil; 194 tk->relief = TKraised; 195 tk->borderwidth = 0; 196 tk->highlightwidth = 1; 197 tk->flag |= Tktakefocus; 198 tkl = TKobj(TkLabel, tk); 199 tkl->ul = -1; 200 tkl->justify = Tkleft; 201 202 switch (btype) { 203 case TKbutton: 204 e = tkbindings(t, tk, bb, nelem(bb)); 205 break; 206 case TKcheckbutton: 207 e = tkbindings(t, tk, cb, nelem(cb)); 208 break; 209 case TKradiobutton: 210 tkl->variable = strdup(tkselbut); 211 e = tkbindings(t, tk, cb, nelem(cb)); 212 break; 213 } 214 215 if (e != nil) { 216 print("tkmkbutton internal error: %s\n", e); 217 tkfreeobj(tk); 218 return nil; 219 } 220 return tk; 221 } 222 223 /* 224 * draw TKbutton, TKcheckbutton, TKradiobutton 225 */ 226 char* 227 tkdrawbutton(Tk *tk, Point orig) 228 { 229 TkEnv *e; 230 TkLabel *tkl; 231 Rectangle r, s, mainr, focusr; 232 int dx, dy, h; 233 Point p, u, v, pp[4]; 234 Image *i, *dst, *cd, *cl, *ct, *img; 235 int relief, bgnd, fgnd; 236 237 e = tk->env; 238 239 dst = tkimageof(tk); 240 if(dst == nil) 241 return nil; 242 243 v.x = tk->act.width + 2*tk->borderwidth; 244 v.y = tk->act.height + 2*tk->borderwidth; 245 246 r.min = ZP; 247 r.max = v; 248 focusr = insetrect(r, tk->borderwidth); 249 mainr = insetrect(focusr, tk->highlightwidth); 250 relief = tk->relief; 251 252 tkl = TKobj(TkLabel, tk); 253 254 fgnd = TkCforegnd; 255 bgnd = TkCbackgnd; 256 if (tk->flag & Tkdisabled) 257 fgnd = TkCdisablefgnd; 258 else if ((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator == BoolF && tkl->check) 259 bgnd = TkCselect; 260 else if (tk->flag & Tkactive) { 261 fgnd = TkCactivefgnd; 262 bgnd = TkCactivebgnd; 263 } 264 265 i = tkitmp(e, r.max, bgnd); 266 if(i == nil) 267 return nil; 268 269 if(tk->flag & Tkactive) 270 draw(i, r, tkgc(e, bgnd), nil, ZP); 271 272 p = mainr.min; 273 h = tkl->h - 2 * tk->highlightwidth; 274 275 dx = tk->act.width - tkl->w - tk->ipad.x; 276 dy = tk->act.height - tkl->h - tk->ipad.y; 277 if((tkl->anchor & (Tknorth|Tksouth)) == 0) 278 p.y += dy/2; 279 else if(tkl->anchor & Tksouth) 280 p.y += dy; 281 282 if((tkl->anchor & (Tkeast|Tkwest)) == 0) 283 p.x += dx/2; 284 else if(tkl->anchor & Tkeast) 285 p.x += dx; 286 287 switch(tk->type) { 288 case TKcheckbutton: 289 if(tkl->indicator == BoolF) { 290 relief = tkl->check? TKsunken: TKraised; 291 break; 292 } 293 u.x = p.x + ButtonBorder; 294 u.y = p.y + ButtonBorder + (h - CheckSpace) / 2; 295 296 cl = tkgc(e, bgnd+TkLightshade); 297 cd = tkgc(e, bgnd+TkDarkshade); 298 tkbevel(i, u, CheckButton, CheckButton, CheckButtonBW, cd, cl); 299 if(tkl->check) { 300 u.x += CheckButtonBW+1; 301 u.y += CheckButtonBW+1; 302 pp[0] = u; 303 pp[0].y += CheckButton/2-1; 304 pp[1] = pp[0]; 305 pp[1].x += 2; 306 pp[1].y += 2; 307 pp[2] = u; 308 pp[2].x += CheckButton/4; 309 pp[2].y += CheckButton-2; 310 pp[3] = u; 311 pp[3].x += CheckButton-2; 312 pp[3].y++; 313 bezspline(i, pp, 4, Enddisc, Enddisc, 1, tkgc(e, TkCforegnd), ZP); 314 } 315 break; 316 case TKradiobutton: 317 if(tkl->indicator == BoolF) { 318 relief = tkl->check? TKsunken: TKraised; 319 break; 320 } 321 u.x = p.x + ButtonBorder; 322 u.y = p.y + ButtonBorder + (h - CheckSpace) / 2; 323 v = Pt(u.x+CheckButton/2,u.y+CheckButton/2); 324 ellipse(i, v, CheckButton/2, CheckButton/2, CheckButtonBW-1, tkgc(e, bgnd+TkDarkshade), ZP); 325 if(tkl->check) 326 fillellipse(i, v, CheckButton/2-2, CheckButton/2-2, tkgc(e, TkCforegnd), ZP); /* could be TkCselect */ 327 break; 328 case TKbutton: 329 if ((tk->flag & (Tkactivated|Tkactive)) == (Tkactivated|Tkactive)) 330 relief = TKsunken; 331 break; 332 } 333 334 p.x += tk->ipad.x/2; 335 p.y += tk->ipad.y/2; 336 u = ZP; 337 if(tk->type == TKbutton && relief == TKsunken) { 338 u.x++; 339 u.y++; 340 } 341 if((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator != BoolF) 342 u.x += CheckSpace; 343 344 img = nil; 345 if (tkl->img != nil && tkl->img->img != nil) 346 img = tkl->img->img; 347 else if (tkl->bitmap != nil) 348 img = tkl->bitmap; 349 if (img != nil) { 350 s.min.x = p.x + Bitpadx; 351 s.min.y = p.y + Bitpady; 352 s.max.x = s.min.x + Dx(img->r); 353 s.max.y = s.min.y + Dy(img->r); 354 s = rectaddpt(s, u); 355 if(tkchanhastype(img->chan, CGrey)) 356 draw(i, s, tkgc(e, fgnd), img, ZP); 357 else 358 draw(i, s, img, nil, ZP); 359 } else if(tkl->text != nil) { 360 u.x += Textpadx; 361 u.y += Textpady; 362 ct = tkgc(e, fgnd); 363 364 p.y += (h - tkl->textheight) / 2; 365 tkdrawstring(tk, i, addpt(u, p), tkl->text, tkl->ul, ct, tkl->justify); 366 } 367 368 // if(tkhaskeyfocus(tk)) 369 // tkbox(i, focusr, tk->highlightwidth, tkgc(e, TkChighlightfgnd)); 370 tkdrawrelief(i, tk, ZP, bgnd, relief); 371 372 p.x = tk->act.x + orig.x; 373 p.y = tk->act.y + orig.y; 374 r = rectaddpt(r, p); 375 draw(dst, r, i, nil, ZP); 376 377 return nil; 378 } 379 380 void 381 tksizebutton(Tk *tk) 382 { 383 int w, h; 384 TkLabel *tkl; 385 386 tkl = TKobj(TkLabel, tk); 387 if(tkl->anchor == 0) 388 tkl->anchor = Tkcenter; 389 390 tksizelabel(tk); /* text, bitmap or image, and highlight */ 391 w = tkl->w; 392 h = tkl->h; 393 394 if((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator != BoolF) { 395 w += CheckSpace; 396 if(h < CheckSpace) 397 h = CheckSpace; 398 } 399 tkl->w = w; 400 tkl->h = h; 401 if((tk->flag & Tksetwidth) == 0) 402 tk->req.width = w; 403 if((tk->flag & Tksetheight) == 0) 404 tk->req.height = h; 405 } 406 407 int 408 tkbuttonmargin(Tk *tk) 409 { 410 TkLabel *tkl; 411 tkl = TKobj(TkLabel, tk); 412 413 switch (tk->type) { 414 case TKbutton: 415 if (tkl->img != nil || tkl->bitmap != nil) 416 return 0; 417 return Textpadx+tk->highlightwidth; 418 case TKcheckbutton: 419 case TKradiobutton: 420 return CheckButton + 2*CheckButtonBW + 2*ButtonBorder; 421 } 422 return tklabelmargin(tk); 423 } 424 425 void 426 tkfreebutton(Tk *tk) 427 { 428 tkfreelabel(tk); 429 } 430 431 static char* 432 tkbuttoncget(Tk *tk, char *arg, char **val) 433 { 434 TkOptab tko[4]; 435 TkLabel *tkl = TKobj(TkLabel, tk); 436 437 tko[0].ptr = tk; 438 tko[0].optab = tkgeneric; 439 tko[1].ptr = tkl; 440 tko[1].optab = tkbutopts; 441 switch(tk->type){ 442 case TKcheckbutton: 443 tko[2].ptr = tkl; 444 tko[2].optab = tkcbopts; 445 break; 446 case TKradiobutton: 447 tko[2].ptr = tkl; 448 tko[2].optab = tkradopts; 449 break; 450 default: 451 tko[2].ptr = nil; 452 break; 453 } 454 tko[3].ptr = nil; 455 return tkgencget(tko, arg, val, tk->env->top); 456 } 457 458 static char* 459 tkbuttonconf(Tk *tk, char *arg, char **val) 460 { 461 char *e; 462 TkGeom g; 463 int bd; 464 TkOptab tko[4]; 465 TkVar *v; 466 TkLabel *tkl = TKobj(TkLabel, tk); 467 468 tko[0].ptr = tk; 469 tko[0].optab = tkgeneric; 470 tko[1].ptr = tkl; 471 tko[1].optab = tkbutopts; 472 switch(tk->type){ 473 case TKcheckbutton: 474 tko[2].ptr = tkl; 475 tko[2].optab = tkcbopts; 476 break; 477 case TKradiobutton: 478 tko[2].ptr = tkl; 479 tko[2].optab = tkradopts; 480 break; 481 default: 482 tko[2].ptr = nil; 483 break; 484 } 485 tko[3].ptr = nil; 486 487 if(*arg == '\0') 488 return tkconflist(tko, val); 489 490 g = tk->req; 491 bd = tk->borderwidth; 492 e = tkparse(tk->env->top, arg, tko, nil); 493 tksizebutton(tk); 494 tkgeomchg(tk, &g, bd); 495 496 tk->dirty = tkrect(tk, 1); 497 tksettransparent(tk, istransparent(tk)); 498 /* 499 * XXX what happens if we're now disabled, but we were in 500 * active state before? 501 */ 502 if (tkl->variable != nil) { 503 v = tkmkvar(tk->env->top, tkl->variable, 0); 504 if (v != nil) { 505 if (v->type != TkVstring) { 506 e = TkNotvt; 507 free(tkl->variable); 508 tkl->variable = nil; 509 } 510 else 511 tkvarchanged(tk, tkl->variable, v->value); 512 } 513 } 514 return e; 515 } 516 517 static int 518 istransparent(Tk *tk) 519 { 520 TkEnv *e = tk->env; 521 return (tkhasalpha(e, TkCbackgnd) || tkhasalpha(e, TkCselectbgnd) || tkhasalpha(e, TkCactivebgnd)); 522 } 523 524 static void 525 tkvarchanged(Tk *tk, char *var, char *val) 526 { 527 TkLabel *tkl; 528 char *sval; 529 530 tkl = TKobj(TkLabel, tk); 531 if (tkl->variable != nil && strcmp(tkl->variable, var) == 0) { 532 sval = tkl->value; 533 if (sval == nil) 534 sval = tk->type == TKcheckbutton ? "1" : ""; 535 tkl->check = (strcmp(val, sval) == 0); 536 tk->dirty = tkrect(tk, 1); 537 tkdirty(tk); 538 } 539 } 540 541 static char* 542 tkbutton1p(Tk *tk, char *arg, char **val) 543 { 544 USED(arg); 545 USED(val); 546 if(tk->flag & Tkdisabled) 547 return nil; 548 tk->flag |= Tkactivated; 549 tk->dirty = tkrect(tk, 1); 550 tkdirty(tk); 551 return nil; 552 } 553 554 static char* 555 tkbutton1r(Tk *tk, char *arg, char **val) 556 { 557 char *e; 558 Point p; 559 Rectangle hitr; 560 561 USED(arg); 562 563 if(tk->flag & Tkdisabled) 564 return nil; 565 e = tkxyparse(tk, &arg, &p); 566 if (e == nil) { 567 hitr.min = ZP; 568 hitr.max.x = tk->act.width + tk->borderwidth*2; 569 hitr.max.y = tk->act.height + tk->borderwidth*2; 570 if(ptinrect(p, hitr) && (tk->flag & Tkactivated)) 571 e = tkbuttoninvoke(tk, nil, val); 572 } 573 tk->flag &= ~Tkactivated; 574 tk->dirty = tkrect(tk, 1); 575 tkdirty(tk); 576 return e; 577 } 578 579 static char* 580 tkbuttonkey(Tk *tk, char *arg, char **val) 581 { 582 int key; 583 584 if(tk->flag & Tkdisabled) 585 return nil; 586 587 key = strtol(arg, nil, 0); 588 if (key == '\n' || key ==' ') 589 return tkbuttoninvoke(tk, nil, val); 590 return nil; 591 } 592 593 static char* 594 tkbuttontoggle(Tk *tk, char *arg, char **val) 595 { 596 char *e; 597 TkLabel *tkl = TKobj(TkLabel, tk); 598 char *v; 599 600 USED(arg); 601 USED(val); 602 if(tk->flag & Tkdisabled) 603 return nil; 604 tkl->check = !tkl->check; 605 if (tkl->check) 606 v = tkl->value ? tkl->value : "1"; 607 else 608 v = tkl->offvalue ? tkl->offvalue : "0"; 609 e = tksetvar(tk->env->top, tkl->variable, v); 610 tk->dirty = tkrect(tk, 0); 611 return e; 612 } 613 614 static char* 615 buttoninvoke(Tk *tk, char **val) 616 { 617 char *e = nil; 618 TkTop *top; 619 TkLabel *tkl = TKobj(TkLabel, tk); 620 621 top = tk->env->top; 622 if (tk->type == TKcheckbutton) 623 e = tkbuttontoggle(tk, "", val); 624 else if (tk->type == TKradiobutton) 625 e = tksetvar(top, tkl->variable, tkl->value); 626 if(e != nil) 627 return e; 628 if(tkl->command != nil) 629 return tkexec(tk->env->top, tkl->command, val); 630 return nil; 631 } 632 633 static void 634 cancelinvoke(Tk *tk, void *v, int cancelled) 635 { 636 int unset; 637 USED(cancelled); 638 USED(v); 639 640 /* if it was active before then leave it active unless cleared since */ 641 if (v) 642 unset = 0; 643 else 644 unset = Tkactive; 645 unset &= (tk->flag & Tkactive); 646 unset |= Tkactivated; 647 tk->flag &= ~unset; 648 tksettransparent(tk, istransparent(tk)); 649 tk->dirty = tkrect(tk, 1); 650 tkdirty(tk); 651 tkupdate(tk->env->top); 652 } 653 654 char* 655 tkbuttoninvoke(Tk *tk, char *arg, char **val) 656 { 657 char *e; 658 USED(arg); 659 660 if(tk->flag & Tkdisabled) 661 return nil; 662 e = buttoninvoke(tk, val); 663 if (e == nil && tk->type == TKbutton && !(tk->flag & Tkactivated)) { 664 tkrepeat(tk, cancelinvoke, (void*)(tk->flag&Tkactive), InvokePause, 0); 665 tk->flag |= Tkactivated | Tkactive; 666 tksettransparent(tk, istransparent(tk)); 667 tk->dirty = tkrect(tk, 1); 668 tkdirty(tk); 669 tkupdate(tk->env->top); 670 } 671 return e; 672 } 673 674 static char* 675 tkbuttonselect(Tk *tk, char *arg, char **val) 676 { 677 char *e, *v; 678 TkLabel *tkl = TKobj(TkLabel, tk); 679 680 USED(arg); 681 USED(val); 682 if (tk->type == TKradiobutton) 683 v = tkl->value; 684 else if (tk->type == TKcheckbutton) { 685 v = tkl->value ? tkl->value : "1"; 686 tkl->check = 1; 687 tk->dirty = tkrect(tk, 0); 688 } else 689 v = nil; 690 e = tksetvar(tk->env->top, tkl->variable, v); 691 if(e != nil) 692 return e; 693 return nil; 694 } 695 696 static char* 697 tkbuttondeselect(Tk *tk, char *arg, char **val) 698 { 699 char *e, *v; 700 TkLabel *tkl = TKobj(TkLabel, tk); 701 702 USED(arg); 703 USED(val); 704 705 if (tk->type == TKcheckbutton) { 706 v = tkl->offvalue ? tkl->offvalue : "0"; 707 tkl->check = 0; 708 tk->dirty = tkrect(tk, 0); 709 } else 710 v = nil; 711 712 e = tksetvar(tk->env->top, tkl->variable, v); 713 if(e != nil) 714 return e; 715 return nil; 716 } 717 718 static 719 TkCmdtab tkbuttoncmd[] = 720 { 721 "cget", tkbuttoncget, 722 "configure", tkbuttonconf, 723 "invoke", tkbuttoninvoke, 724 "tkButton1P", tkbutton1p, 725 "tkButton1R", tkbutton1r, 726 "tkButtonKey", tkbuttonkey, 727 nil 728 }; 729 730 static 731 TkCmdtab tkchkbuttoncmd[] = 732 { 733 "cget", tkbuttoncget, 734 "configure", tkbuttonconf, 735 "invoke", tkbuttoninvoke, 736 "select", tkbuttonselect, 737 "deselect", tkbuttondeselect, 738 "toggle", tkbuttontoggle, 739 "tkButtonKey", tkbuttonkey, 740 nil 741 }; 742 743 static 744 TkCmdtab tkradbuttoncmd[] = 745 { 746 "cget", tkbuttoncget, 747 "configure", tkbuttonconf, 748 "invoke", tkbuttoninvoke, 749 "select", tkbuttonselect, 750 "deselect", tkbuttondeselect, 751 "tkButtonKey", tkbuttonkey, 752 nil 753 }; 754 755 TkMethod buttonmethod = { 756 "button", 757 tkbuttoncmd, 758 tkfreebutton, 759 tkdrawbutton, 760 nil, 761 tklabelgetimgs 762 }; 763 764 TkMethod checkbuttonmethod = { 765 "checkbutton", 766 tkchkbuttoncmd, 767 tkfreebutton, 768 tkdrawbutton, 769 nil, 770 tklabelgetimgs, 771 nil, 772 nil, 773 nil, 774 nil, 775 nil, 776 nil, 777 tkvarchanged 778 }; 779 780 TkMethod radiobuttonmethod = { 781 "radiobutton", 782 tkradbuttoncmd, 783 tkfreebutton, 784 tkdrawbutton, 785 nil, 786 nil, 787 nil, 788 nil, 789 nil, 790 nil, 791 nil, 792 nil, 793 tkvarchanged 794 }; 795