1 #include "lib9.h" 2 #include "draw.h" 3 #include "tk.h" 4 5 /* 6 * XXX TODO 7 * - grid rowcget|columncget 8 * - grid columnconfigure/rowconfigure accepts a list of indexes? 9 */ 10 11 #define O(t, e) ((long)(&((t*)0)->e)) 12 13 typedef struct TkGridparam TkGridparam; 14 typedef struct TkBeamparam TkBeamparam; 15 16 struct TkGridparam{ 17 Point span; 18 Tk* in; 19 Point pad; 20 Point ipad; 21 char *row; 22 char *col; 23 int sticky; 24 }; 25 26 struct TkBeamparam{ 27 int minsize; 28 int maxsize; 29 int weight; 30 int pad; 31 char *name; 32 int equalise; 33 }; 34 35 static 36 TkOption opts[] = 37 { 38 "padx", OPTnndist, O(TkGridparam, pad.x), nil, 39 "pady", OPTnndist, O(TkGridparam, pad.y), nil, 40 "ipadx", OPTnndist, O(TkGridparam, ipad.x), nil, 41 "ipady", OPTnndist, O(TkGridparam, ipad.y), nil, 42 "in", OPTwinp, O(TkGridparam, in), nil, 43 "row", OPTtext, O(TkGridparam, row), nil, 44 "column", OPTtext, O(TkGridparam, col), nil, 45 "rowspan", OPTnndist, O(TkGridparam, span.y), nil, 46 "columnspan", OPTnndist, O(TkGridparam, span.x), nil, 47 "sticky", OPTsticky, O(TkGridparam, sticky), nil, 48 nil 49 }; 50 51 static 52 TkOption beamopts[] = 53 { 54 "minsize", OPTnndist, O(TkBeamparam, minsize), nil, 55 "maxsize", OPTnndist, O(TkBeamparam, maxsize), nil, 56 "weight", OPTnndist, O(TkBeamparam, weight), nil, 57 "pad", OPTnndist, O(TkBeamparam, pad), nil, 58 "name", OPTtext, O(TkBeamparam, name), nil, 59 "equalise", OPTstab, O(TkBeamparam, equalise), tkbool, 60 nil 61 }; 62 63 void 64 printgrid(TkGrid *grid) 65 { 66 int x, y; 67 Point dim; 68 69 dim = grid->dim; 70 print("grid %P\n", grid->dim); 71 print(" row heights: "); 72 for(y = 0; y < dim.y; y++) 73 print("%d[%d,%d,w%d,p%d]%s ", 74 grid->rows[y].act, 75 grid->rows[y].minsize, 76 grid->rows[y].maxsize < 0x7fffffff ? grid->rows[y].maxsize : -1, 77 grid->rows[y].weight, 78 grid->rows[y].pad, 79 grid->rows[y].name ? grid->rows[y].name : ""); 80 print("\n"); 81 print(" col widths: "); 82 for(x = 0; x < dim.x; x++) 83 print("%d[%d,%d,w%d,p%d]%s ", 84 grid->cols[x].act, 85 grid->cols[x].minsize, 86 grid->cols[x].maxsize < 0x7fffffff ? grid->cols[x].maxsize : -1, 87 grid->cols[x].weight, 88 grid->cols[x].pad, 89 grid->cols[x].name ? grid->cols[x].name : ""); 90 print("\n"); 91 for(y = 0; y < dim.y; y++){ 92 print(" row %d: ", y); 93 for(x = 0; x < dim.x; x++){ 94 print("%p;", grid->cells[y][x].tk); 95 print("%s%P\t", grid->cells[y][x].tk?grid->cells[y][x].tk->name->name:"(nil)", 96 grid->cells[y][x].span); 97 } 98 print("\n"); 99 } 100 } 101 102 static void 103 tkgridsetopt(TkGridparam *p, Tk *tk) 104 { 105 if(p->pad.x != -1) 106 tk->pad.x = p->pad.x*2; 107 if(p->pad.y != -1) 108 tk->pad.y = p->pad.y*2; 109 if(p->ipad.x != -1) 110 tk->ipad.x = p->ipad.x*2; 111 if(p->ipad.y != -1) 112 tk->ipad.y = p->ipad.y*2; 113 if(p->sticky != -1) 114 tk->flag = (tk->flag & ~(Tkanchor|Tkfill)) | (p->sticky & (Tkanchor|Tkfill)); 115 } 116 117 static void 118 initbeam(TkGridbeam *beam, int n) 119 { 120 int i; 121 memset(beam, 0, n * sizeof(TkGridbeam)); 122 for(i = 0; i < n; i++) 123 beam[i].maxsize = 0x7fffffff; 124 } 125 126 static char* 127 ensuregridsize(TkGrid *grid, Point dim) 128 { 129 TkGridcell **cells, *cellrow; 130 TkGridbeam *cols, *rows; 131 Point olddim; 132 int i; 133 olddim = grid->dim; 134 if(dim.x < olddim.x) 135 dim.x = olddim.x; 136 if(dim.y < olddim.y) 137 dim.y = olddim.y; 138 if(dim.y > olddim.y){ 139 cells = realloc(grid->cells, sizeof(TkGridcell*)*dim.y); 140 if(cells == nil) 141 return TkNomem; 142 grid->cells = cells; 143 for(i = olddim.y; i < dim.y; i++){ 144 cells[i] = malloc(sizeof(TkGridcell)*dim.x); 145 if(cells[i] == nil){ 146 while(--i >= olddim.y) 147 free(cells[i]); 148 return TkNomem; 149 } 150 } 151 rows = realloc(grid->rows, sizeof(TkGridbeam)*dim.y); 152 if(rows == nil) 153 return TkNomem; 154 grid->rows = rows; 155 initbeam(rows + olddim.y, dim.y - olddim.y); 156 grid->dim.y = dim.y; 157 } 158 159 if(dim.x > olddim.x){ 160 /* 161 * any newly allocated rows will have the correct number of 162 * columns, so we don't need to reallocate them 163 */ 164 cells = grid->cells; 165 for(i = 0; i < olddim.y; i++){ 166 cellrow = realloc(cells[i], sizeof(TkGridcell) * dim.x); 167 if(cellrow == nil) 168 return TkNomem; /* leak some earlier rows, but not permanently */ 169 memset(cellrow + olddim.x, 0, (dim.x-olddim.x)*sizeof(TkGridcell)); 170 cells[i] = cellrow; 171 } 172 cols = realloc(grid->cols, sizeof(TkGridbeam)*dim.x); 173 if(cols == nil) 174 return TkNomem; 175 initbeam(cols + olddim.x, dim.x - olddim.x); 176 grid->cols = cols; 177 grid->dim.x = dim.x; 178 } 179 return nil; 180 } 181 182 static TkGridbeam* 183 delbeams(TkGridbeam *beam, int nb, int x0, int x1) 184 { 185 int i; 186 TkGridbeam *b; 187 for(i = x0; i < x1; i++) 188 free(beam[i].name); 189 memmove(&beam[x0], &beam[x1], sizeof(TkGridbeam) * (nb-x1)); 190 b = realloc(beam, sizeof(TkGridbeam) * (nb-(x1-x0))); 191 return b ? b : beam; 192 } 193 194 static void 195 delrows(TkGrid *grid, int y0, int y1) 196 { 197 TkGridcell **cells; 198 memmove(grid->cells+y0, grid->cells+y1, sizeof(TkGridcell*) * (grid->dim.y-y1)); 199 grid->dim.y -= (y1 - y0); 200 cells = realloc(grid->cells, sizeof(TkGridcell*) * grid->dim.y); 201 if(cells != nil || grid->dim.y == 0) 202 grid->cells = cells; /* can realloc to a smaller size ever fail? */ 203 } 204 205 static void 206 delcols(TkGrid *grid, int x0, int x1) 207 { 208 TkGridcell **cells, *row; 209 int y, ndx; 210 Point dim; 211 dim = grid->dim; 212 ndx = dim.x - (x1 - x0); 213 cells = grid->cells; 214 for(y = 0; y < dim.y; y++){ 215 row = cells[y]; 216 memmove(row+x0, row+x1, sizeof(TkGridcell) * (dim.x - x1)); 217 row = realloc(row, sizeof(TkGridcell) * ndx); 218 if(row != nil || ndx == 0) 219 cells[y] = row; 220 } 221 grid->dim.x = ndx; 222 } 223 224 /* 225 * insert items into rows/cols; the beam has already been expanded appropriately. 226 */ 227 void 228 insbeams(TkGridbeam *beam, int nb, int x, int n) 229 { 230 memmove(&beam[x+n], &beam[x], sizeof(TkGridbeam)*(nb-x-n)); 231 initbeam(beam+x, n); 232 } 233 234 static char* 235 insrows(TkGrid *grid, int y0, int n) 236 { 237 Point olddim; 238 char *e; 239 TkGridcell **cells, *tmp; 240 int y; 241 242 olddim = grid->dim; 243 if(y0 > olddim.y){ 244 n = y0 + n - olddim.y; 245 y0 = olddim.y; 246 } 247 248 e = ensuregridsize(grid, Pt(olddim.x, olddim.y + n)); 249 if(e != nil) 250 return e; 251 /* 252 * we know the extra rows will have been filled 253 * with blank, properly allocated rows, so just swap 'em with the 254 * ones that need moving. 255 */ 256 cells = grid->cells; 257 for(y = olddim.y - 1; y >= y0; y--){ 258 tmp = cells[y + n]; 259 cells[y + n] = cells[y]; 260 cells[y] = tmp; 261 } 262 insbeams(grid->rows, grid->dim.y, y0, n); 263 return nil; 264 } 265 266 static char* 267 inscols(TkGrid *grid, int x0, int n) 268 { 269 TkGridcell **cells; 270 Point olddim; 271 int y; 272 char *e; 273 274 olddim = grid->dim; 275 if(x0 > olddim.x){ 276 n = x0 + n - olddim.x; 277 x0 = olddim.x; 278 } 279 280 e = ensuregridsize(grid, Pt(olddim.x + n, olddim.y)); 281 if(e != nil) 282 return e; 283 284 cells = grid->cells; 285 for(y = 0; y < olddim.y; y++){ 286 memmove(cells[y] + x0 + n, cells[y] + x0, sizeof(TkGridcell) * (olddim.x - x0)); 287 memset(cells[y] + x0, 0, sizeof(TkGridcell) * n); 288 } 289 insbeams(grid->cols, grid->dim.x, x0, n); 290 return nil; 291 } 292 293 static int 294 maximum(int a, int b) 295 { 296 if(a > b) 297 return a; 298 return b; 299 } 300 301 /* 302 * return the width of cols/rows between x0 and x1 in the beam, 303 * excluding the padding at either end, but including padding in the middle. 304 */ 305 static int 306 beamsize(TkGridbeam *cols, int x0, int x1) 307 { 308 int tot, fpad, x; 309 310 if(x0 >= x1) 311 return 0; 312 313 tot = cols[x0].act; 314 fpad = cols[x0].pad; 315 for(x = x0 + 1; x < x1; x++){ 316 tot += cols[x].act + maximum(cols[x].pad, fpad); 317 fpad = cols[x].pad; 318 } 319 return tot; 320 } 321 322 /* 323 * return starting position of cell index on beam, relative 324 * to top-left of grid 325 */ 326 static int 327 beamcellpos(TkGridbeam *beam, int blen, int index) 328 { 329 int x; 330 if(blen == 0 || index >= blen || index < 0) 331 return 0; 332 x = beam[0].pad + beamsize(beam, 0, index); 333 if(index > 0) 334 x += maximum(beam[index-1].pad, beam[index].pad); 335 return x; 336 } 337 338 static Rectangle 339 cellbbox(TkGrid *grid, Point pos) 340 { 341 Point dim; 342 Rectangle r; 343 344 dim = grid->dim; 345 if(pos.x > dim.x) 346 pos.x = dim.x; 347 if(pos.y > dim.y) 348 pos.y = dim.y; 349 350 r.min.x = beamcellpos(grid->cols, dim.x, pos.x); 351 r.min.y = beamcellpos(grid->rows, dim.y, pos.y); 352 if(pos.x == dim.x) 353 r.max.x = r.min.x; 354 else 355 r.max.x = r.min.x + grid->cols[pos.x].act; 356 if(pos.y == dim.y) 357 r.max.y = r.min.y; 358 else 359 r.max.y = r.min.y + grid->rows[pos.y].act; 360 return rectaddpt(r, grid->origin); 361 } 362 363 /* 364 * return true ifthere are any spanning cells covering row _index_ 365 */ 366 static int 367 gridrowhasspan(TkGrid *grid, int index) 368 { 369 int i, d; 370 Point dim; 371 TkGridcell *cell; 372 373 dim = grid->dim; 374 if(index > 0 && index < dim.y){ 375 for(i = 0; i < dim.x; i++){ 376 cell = &grid->cells[index][i]; 377 if(cell->tk != nil){ 378 d = cell->span.x; 379 if(d == 0) 380 return 1; 381 i += d - 1; 382 } 383 } 384 } 385 return 0; 386 } 387 388 /* 389 * return true ifthere are any spanning cells covering column _index_ 390 */ 391 static int 392 gridcolhasspan(TkGrid *grid, int index) 393 { 394 int i, d; 395 Point dim; 396 TkGridcell *cell; 397 398 dim = grid->dim; 399 if(index > 0 && index < dim.x){ 400 for(i = 0; i < dim.y; i++){ 401 cell = &grid->cells[i][index]; 402 if(cell->tk != nil){ 403 d = cell->span.y; 404 if(d == 0) 405 return 1; 406 i += d - 1; 407 } 408 } 409 } 410 return 0; 411 } 412 413 /* 414 * find cell that's spanning the grid position p 415 */ 416 static int 417 findspan(TkGrid *grid, Point p, Point *cp) 418 { 419 Point dim; 420 TkGridcell **cells; 421 Tk *tk; 422 423 dim = grid->dim; 424 cells = grid->cells; 425 426 if(p.x < 0 || p.y < 0 || p.x >= dim.x || p.y >= dim.y) 427 return 0; 428 429 if(cells[p.y][p.x].tk == nil) 430 return 0; 431 432 if(cells[p.y][p.x].span.x == 0){ 433 tk = cells[p.y][p.x].tk; 434 for(; p.y >= 0; p.y--) 435 if(cells[p.y][p.x].tk != tk) 436 break; 437 p.y++; 438 for(; p.x >= 0; p.x--) 439 if(cells[p.y][p.x].tk != tk) 440 break; 441 p.x++; 442 } 443 *cp = p; 444 return 1; 445 } 446 447 static int 448 parsegridindex(TkGridbeam *beam, int blen, char *s) 449 { 450 int n, i; 451 char *e; 452 453 if(s[0] == '\0') 454 return -1; 455 456 n = strtol(s, &e, 10); 457 if(*e == '\0') 458 return n; 459 460 if(strcmp(s, "end") == 0) 461 return blen; 462 463 for(i = 0; i < blen; i++) 464 if(beam[i].name != nil && strcmp(beam[i].name, s) == 0) 465 return i; 466 return -1; 467 } 468 469 static char* 470 tkgridconfigure(TkTop *t, TkGridparam *p, TkName *names) 471 { 472 TkGrid *grid; 473 TkGridcell **cells; 474 TkName *n; 475 Tk *tkf, *tkp; 476 Point dim, pos, q, span, startpos; 477 int maxcol, c, i, j, x; 478 char *e; 479 480 if(names == nil) 481 return nil; 482 483 if(p->span.x < 1 || p->span.y < 1) 484 return TkBadvl; 485 486 tkf = nil; 487 488 maxcol = 0; 489 for(n = names; n; n = n->link){ 490 c = n->name[0]; 491 if((c=='-' || c=='^' || c=='x') && n->name[1] == '\0'){ 492 maxcol++; 493 continue; 494 } 495 tkp = tklook(t, n->name, 0); 496 if(tkp == nil){ 497 tkerr(t, n->name); 498 return TkBadwp; 499 } 500 if(tkp->flag & Tkwindow) 501 return TkIstop; 502 if(tkp->parent != nil) 503 return TkWpack; 504 505 /* 506 * unpacking now does give an non-reversible side effect 507 * ifthere's an error encountered later, but also means 508 * that a widget repacked in the same grid will 509 * have its original cell still available 510 */ 511 if(tkp->master != nil){ 512 tkpackqit(tkp->master); 513 tkdelpack(tkp); 514 } 515 if(tkf == nil) 516 tkf = tkp; 517 n->obj = tkp; 518 tkp->flag &= ~Tkgridpack; 519 maxcol += p->span.x; 520 } 521 522 if(p->in == nil && tkf != nil) 523 p->in = tklook(t, tkf->name->name, 1); 524 525 if(p->in == nil) 526 return TkNomaster; 527 528 grid = p->in->grid; 529 if(grid == nil && p->in->slave != nil) 530 return TkNotgrid; 531 532 if(grid == nil){ 533 grid = malloc(sizeof(TkGrid)); 534 if(grid == nil) 535 return TkNomem; 536 p->in->grid = grid; 537 } 538 539 dim = grid->dim; 540 pos = ZP; 541 if(p->row != nil){ 542 pos.y = parsegridindex(grid->rows, dim.y, p->row); 543 if(pos.y < 0) 544 return TkBadix; 545 } 546 if(p->col != nil){ 547 pos.x = parsegridindex(grid->cols, dim.x, p->col); 548 if(pos.x < 0) 549 return TkBadix; 550 } 551 /* 552 * ifrow is not specified, find first unoccupied row 553 */ 554 if(p->row == nil){ 555 for(pos.y = 0; pos.y < dim.y; pos.y++){ 556 for(x = 0; x < dim.x; x++) 557 if(grid->cells[pos.y][x].tk != nil) 558 break; 559 if(x == dim.x) 560 break; 561 } 562 } 563 e = ensuregridsize(grid, Pt(pos.x + maxcol, pos.y + p->span.y)); 564 if(e != nil) 565 return e; 566 cells = grid->cells; 567 568 startpos = pos; 569 /* 570 * check that all our grid cells are empty, and that row/col spans 571 * are well formed 572 */ 573 n = names; 574 while(n != nil){ 575 c = n->name[0]; 576 switch (c){ 577 case 'x': 578 n = n->link; 579 pos.x++; 580 break; 581 case '^': 582 if(findspan(grid, Pt(pos.x, pos.y - 1), &q) == 0) 583 return TkBadspan; 584 span = cells[q.y][q.x].span; 585 for(i = 0; i < span.x; i++){ 586 if(n == nil || strcmp(n->name, "^")) 587 return TkBadspan; 588 if(cells[pos.y][pos.x + i].tk != nil) 589 return TkBadgridcell; 590 n = n->link; 591 } 592 pos.x += span.x; 593 break; 594 case '-': 595 return TkBadspan; 596 case '.': 597 tkp = n->obj; 598 if(tkisslave(p->in, tkp)) 599 return TkRecur; 600 n = n->link; 601 if(tkp->flag & Tkgridpack) 602 return TkWpack; 603 tkp->flag |= Tkgridpack; 604 span = p->span; 605 for(; n != nil && strcmp(n->name, "-") == 0; n = n->link) 606 span.x++; 607 for(i = pos.x; i < pos.x + span.x; i++) 608 for(j = pos.y; j < pos.y + span.y; j++) 609 if(cells[j][i].tk != nil) 610 return TkBadgridcell; 611 pos.x = i; 612 break; 613 } 614 } 615 616 /* 617 * actually insert the items into the grid 618 */ 619 n = names; 620 pos = startpos; 621 while(n != nil){ 622 c = n->name[0]; 623 switch (c){ 624 case 'x': 625 n = n->link; 626 pos.x++; 627 break; 628 case '^': 629 findspan(grid, Pt(pos.x, pos.y - 1), &q); 630 span = cells[q.y][q.x].span; 631 tkf = cells[q.y][q.x].tk; 632 if(q.y + span.y == pos.y) 633 cells[q.y][q.x].span.y++; 634 635 for(i = 0; i < span.x; i++){ 636 cells[pos.y][pos.x++].tk = tkf; 637 n = n->link; 638 } 639 break; 640 case '.': 641 tkf = n->obj; 642 n = n->link; 643 span = p->span; 644 for(; n != nil && strcmp(n->name, "-") == 0; n = n->link) 645 span.x++; 646 for(i = pos.x; i < pos.x + span.x; i++) 647 for(j = pos.y; j < pos.y + span.y; j++) 648 cells[j][i].tk = tkf; 649 cells[pos.y][pos.x].span = span; 650 tkf->master = p->in; 651 tkf->next = p->in->slave; 652 p->in->slave = tkf; 653 if(p->in->flag & Tksubsub) 654 tksetbits(tkf, Tksubsub); 655 tkgridsetopt(p, tkf); 656 pos.x = i; 657 break; 658 } 659 } 660 tkpackqit(p->in); 661 tkrunpack(t); 662 return nil; 663 } 664 665 void 666 tkgriddelslave(Tk *tk) 667 { 668 int y, x, yy; 669 TkGrid *grid; 670 TkGridcell **cells, *cell; 671 Point dim, span; 672 673 if(tk == nil || tk->master == nil || tk->master->grid == nil) 674 return; 675 grid = tk->master->grid; 676 cells = grid->cells; 677 dim = grid->dim; 678 for(y = 0; y < dim.y; y++){ 679 for(x = 0; x < dim.x; x++){ 680 cell = &cells[y][x]; 681 if(cell->tk == tk){ 682 span = cell->span; 683 for(yy = y; yy < y + span.y; yy++) 684 memset(cells[yy] + x, 0, span.x * sizeof(TkGridcell)); 685 return; 686 } 687 } 688 } 689 } 690 691 char* 692 tkgetgridmaster(TkTop *t, char **arg, char *buf, char *ebuf, Tk **master) 693 { 694 TkGrid *grid; 695 696 *arg = tkword(t, *arg, buf, ebuf, nil); 697 *master = tklook(t, buf, 0); 698 if(*master == nil) 699 return TkBadwp; 700 grid = (*master)->grid; 701 if(grid == nil && (*master)->slave != nil) 702 return TkNotgrid; 703 return nil; 704 } 705 706 static int 707 gridfindloc(TkGridbeam *beam, int blen, int f) 708 { 709 int x, i, fpad; 710 if(blen == 0 || f < 0) 711 return -1; 712 713 fpad = 0; 714 x = 0; 715 for(i = 0; i < blen; i++){ 716 x += maximum(fpad, beam[i].pad); 717 if(x <= f && f < x + beam[i].act) 718 return i; 719 x += beam[i].act; 720 } 721 return -1; 722 } 723 724 static char* 725 tkgridcellinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf) 726 { 727 /* grid cellinfo master x y */ 728 Tk *master; 729 char *e; 730 Point p; 731 TkGrid *grid; 732 TkGridcell **cells; 733 734 e = tkgetgridmaster(t, &arg, buf, ebuf, &master); 735 if(e != nil || master->grid == nil) 736 return e; 737 grid = master->grid; 738 739 e = tkfracword(t, &arg, &p.x, nil); 740 if(e != nil) 741 return e; 742 e = tkfracword(t, &arg, &p.y, nil); 743 if(e != nil) 744 return e; 745 746 p.x = TKF2I(p.x); 747 p.y = TKF2I(p.y); 748 if(p.x < 0 || p.x >= grid->dim.x || p.y < 0 || p.y >= grid->dim.y) 749 return nil; 750 751 if(!findspan(grid, p, &p)) 752 return nil; 753 754 cells = grid->cells; 755 return tkvalue(val, "%s -in %s -column %d -row %d -columnspan %d -rowspan %d", 756 cells[p.y][p.x].tk->name->name, 757 cells[p.y][p.x].tk->master->name->name, p.x, p.y, 758 cells[p.y][p.x].span.x, cells[p.y][p.x].span.y); 759 } 760 761 static char* 762 tkgridlocation(TkTop *t, char *arg, char **val, char *buf, char *ebuf) 763 { 764 /* grid location master x y */ 765 Tk *master; 766 char *e; 767 Point p; 768 int col, row; 769 TkGrid *grid; 770 771 e = tkgetgridmaster(t, &arg, buf, ebuf, &master); 772 if(e != nil || master->grid == nil) 773 return e; 774 grid = master->grid; 775 776 e = tkfracword(t, &arg, &p.x, nil); 777 if(e != nil) 778 return e; 779 e = tkfracword(t, &arg, &p.y, nil); 780 if(e != nil) 781 return e; 782 783 p.x = TKF2I(p.x); 784 p.y = TKF2I(p.y); 785 786 p = subpt(p, grid->origin); 787 col = gridfindloc(grid->cols, grid->dim.x, p.x); 788 row = gridfindloc(grid->rows, grid->dim.y, p.y); 789 if(col < 0 || row < 0) 790 return nil; 791 return tkvalue(val, "%d %d", col, row); 792 } 793 794 static char* 795 tkgridinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf) 796 { 797 Tk *tk; 798 TkGrid *grid; 799 int x, y; 800 Point dim; 801 TkGridcell *row; 802 803 tkword(t, arg, buf, ebuf, nil); 804 tk = tklook(t, buf, 0); 805 if(tk == nil) 806 return TkBadwp; 807 if(tk->master == nil || tk->master->grid == nil) 808 return TkNotgrid; 809 grid = tk->master->grid; 810 dim = grid->dim; 811 for(y = 0; y < dim.y; y++){ 812 row = grid->cells[y]; 813 for(x = 0; x < dim.x; x++) 814 if(row[x].tk == tk) 815 goto Found; 816 } 817 return TkNotgrid; /* should not happen */ 818 Found: 819 return tkvalue(val, "-in %s -column %d -row %d -columnspan %d -rowspan %d", 820 tk->master->name->name, x, y, grid->cells[y][x].span.x, grid->cells[y][x].span.y); 821 } 822 823 static char* 824 tkgridforget(TkTop *t, char *arg, char *buf, char *ebuf) 825 { 826 Tk *tk; 827 for(;;){ 828 arg = tkword(t, arg, buf, ebuf, nil); 829 if(arg == nil || buf[0] == '\0') 830 break; 831 tk = tklook(t, buf, 0); 832 if(tk == nil){ 833 tkrunpack(t); 834 tkerr(t, buf); 835 return TkBadwp; 836 } 837 tkpackqit(tk->master); 838 tkdelpack(tk); 839 } 840 tkrunpack(t); 841 return nil; 842 } 843 844 static char* 845 tkgridslaves(TkTop *t, char *arg, char **val, char *buf, char *ebuf) 846 { 847 Tk *master, *tk; 848 char *fmt; 849 int i, isrow, index; 850 TkGrid *grid; 851 TkGridcell *cell; 852 char *e; 853 e = tkgetgridmaster(t, &arg, buf, ebuf, &master); 854 if(e != nil || master->grid == nil) 855 return e; 856 grid = master->grid; 857 arg = tkword(t, arg, buf, ebuf, nil); 858 fmt = "%s"; 859 if(buf[0] == '\0'){ 860 for(tk = master->slave; tk != nil; tk = tk->next){ 861 if(tk->name != nil){ 862 e = tkvalue(val, fmt, tk->name->name); 863 if(e != nil) 864 return e; 865 fmt = " %s"; 866 } 867 } 868 return nil; 869 } 870 if(strcmp(buf, "-row") == 0) 871 isrow = 1; 872 else if(strcmp(buf, "-column") == 0) 873 isrow = 0; 874 else 875 return TkBadop; 876 tkword(t, arg, buf, ebuf, nil); 877 if(isrow) 878 index = parsegridindex(grid->rows, grid->dim.y, buf); 879 else 880 index = parsegridindex(grid->cols, grid->dim.x, buf); 881 if(index < 0) 882 return TkBadix; 883 if(isrow){ 884 if(index >= grid->dim.y) 885 return nil; 886 for(i = 0; i < grid->dim.x; i++){ 887 cell = &grid->cells[index][i]; 888 if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){ 889 e = tkvalue(val, fmt, cell->tk->name->name); 890 if(e != nil) 891 return e; 892 fmt = " %s"; 893 } 894 } 895 } else{ 896 if(index >= grid->dim.x) 897 return nil; 898 for(i = 0; i < grid->dim.y; i++){ 899 cell = &grid->cells[i][index]; 900 if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){ 901 e = tkvalue(val, fmt, cell->tk->name->name); 902 if(e != nil) 903 return e; 904 fmt = " %s"; 905 } 906 } 907 } 908 909 return nil; 910 } 911 912 static char* 913 tkgriddelete(TkTop *t, char *arg, char *buf, char *ebuf, int delrow) 914 { 915 Tk *master, **l, *f; 916 TkGrid *grid; 917 TkGridbeam *beam; 918 int blen, i0, i1, x, y; 919 Point dim; 920 TkGridcell **cells; 921 char *e; 922 923 /* 924 * grid (columndelete|rowdelete) master index0 ?index1? 925 */ 926 927 e = tkgetgridmaster(t, &arg, buf, ebuf, &master); 928 if(e != nil || master->grid == nil) 929 return e; 930 grid = master->grid; 931 932 if(delrow){ 933 beam = grid->rows; 934 blen = grid->dim.y; 935 } else{ 936 beam = grid->cols; 937 blen = grid->dim.x; 938 } 939 940 arg = tkword(t, arg, buf, ebuf, nil); 941 i0 = parsegridindex(beam, blen, buf); 942 if(i0 < 0) 943 return TkBadix; 944 945 tkword(t, arg, buf, ebuf, nil); 946 if(buf[0] == '\0') 947 i1 = i0 + 1; 948 else 949 i1 = parsegridindex(beam, blen, buf); 950 if(i1 < 0 || i0 > i1) 951 return TkBadix; 952 if(i0 > blen || i0 == i1) 953 return nil; 954 if(i1 > blen) 955 i1 = blen; 956 cells = grid->cells; 957 dim = grid->dim; 958 if(delrow){ 959 if(gridrowhasspan(grid, i0) || gridrowhasspan(grid, i1)) 960 return TkBadgridcell; 961 for(y = i0; y < i1; y++) 962 for(x = 0; x < dim.x; x++) 963 if(cells[y][x].tk != nil) 964 cells[y][x].tk->flag |= Tkgridremove; 965 delrows(grid, i0, i1); 966 grid->rows = delbeams(beam, blen, i0, i1); 967 } else{ 968 if(gridcolhasspan(grid, i0) || gridcolhasspan(grid, i1)) 969 return TkBadgridcell; 970 for(y = 0; y < dim.y; y++) 971 for(x = i0; x < i1; x++) 972 if(cells[y][x].tk != nil) 973 cells[y][x].tk->flag |= Tkgridremove; 974 delcols(grid, i0, i1); 975 grid->cols = delbeams(beam, blen, i0, i1); 976 } 977 l = &master->slave; 978 for(f = *l; f; f = f->next){ 979 if(f->flag & Tkgridremove){ 980 *l = f->next; 981 f->master = nil; 982 f->flag &= ~Tkgridremove; 983 } else 984 l = &f->next; 985 } 986 tkpackqit(master); 987 tkrunpack(t); 988 return nil; 989 } 990 991 992 static char* 993 tkgridinsert(TkTop *t, char *arg, char *buf, char *ebuf, int insertrow) 994 { 995 int index, count; 996 Point dim; 997 Tk *master; 998 TkGrid *grid; 999 int gotarg; 1000 char *e; 1001 1002 /* 1003 * grid (rowinsert|columninsert) master index ?count? 1004 * it's an error ifthe insert splits any spanning cells. 1005 */ 1006 e = tkgetgridmaster(t, &arg, buf, ebuf, &master); 1007 if(e != nil || master->grid == nil) 1008 return e; 1009 grid = master->grid; 1010 dim = grid->dim; 1011 1012 arg = tkword(t, arg, buf, ebuf, nil); 1013 if(insertrow) 1014 index = parsegridindex(grid->rows, dim.y, buf); 1015 else 1016 index = parsegridindex(grid->cols, dim.x, buf); 1017 if(index < 0 || index > (insertrow ? dim.y : dim.x)) 1018 return TkBadix; 1019 1020 tkword(t, arg, buf, ebuf, &gotarg); 1021 if(gotarg){ 1022 count = strtol(buf, &buf, 10); 1023 if(buf[0] != '\0' || count < 0) 1024 return TkBadvl; 1025 } else 1026 count = 1; 1027 1028 /* 1029 * check that we're not splitting any spanning cells 1030 */ 1031 if(insertrow){ 1032 if(gridrowhasspan(grid, index)) 1033 return TkBadgridcell; 1034 e = insrows(grid, index, count); 1035 } else{ 1036 if(gridcolhasspan(grid, index)) 1037 return TkBadgridcell; 1038 e = inscols(grid, index, count); 1039 } 1040 tkpackqit(master); 1041 tkrunpack(t); 1042 return e; 1043 } 1044 1045 /* 1046 * (rowconfigure|columnconfigure) master index ?-option value ...? 1047 */ 1048 static char* 1049 tkbeamconfigure(TkTop *t, char *arg, int isrow) 1050 { 1051 TkBeamparam p; 1052 TkOptab tko[2]; 1053 TkName *names; 1054 Tk *master; 1055 int index; 1056 TkGrid *grid; 1057 TkGridbeam *beam; 1058 Point dim; 1059 char *e; 1060 1061 p.equalise = BoolX; 1062 p.name = nil; 1063 p.weight = -1; 1064 p.minsize = -1; 1065 p.maxsize = -1; 1066 p.pad = -1; 1067 1068 tko[0].ptr = &p; 1069 tko[0].optab = beamopts; 1070 tko[1].ptr = nil; 1071 1072 names = nil; 1073 e = tkparse(t, arg, tko, &names); 1074 if(e != nil) 1075 return e; 1076 1077 if(names == nil || names->link == nil) 1078 return TkBadvl; 1079 1080 master = tklook(t, names->name, 0); 1081 if(master == nil) 1082 return TkBadwp; 1083 1084 grid = master->grid; 1085 if(grid == nil){ 1086 if(master->slave != nil) 1087 return TkNotgrid; 1088 grid = master->grid = malloc(sizeof(TkGrid)); 1089 if(grid == nil){ 1090 tkfreename(names); 1091 return TkNomem; 1092 } 1093 } 1094 1095 if(isrow){ 1096 index = parsegridindex(grid->rows, grid->dim.y, names->link->name); 1097 } else 1098 index = parsegridindex(grid->cols, grid->dim.x, names->link->name); 1099 if(index < 0){ 1100 e = TkBadix; 1101 goto Error; 1102 } 1103 if(isrow) 1104 dim = Pt(grid->dim.x, index + 1); 1105 else 1106 dim = Pt(index + 1, grid->dim.y); 1107 e = ensuregridsize(grid, dim); 1108 if(e != nil) 1109 goto Error; 1110 1111 if(isrow) 1112 beam = &grid->rows[index]; 1113 else 1114 beam = &grid->cols[index]; 1115 1116 if(p.minsize >= 0) 1117 beam->minsize = p.minsize; 1118 if(p.maxsize >= 0) 1119 beam->maxsize = p.maxsize; 1120 if(p.weight >= 0) 1121 beam->weight = p.weight; 1122 if(p.pad >= 0) 1123 beam->pad = p.pad; 1124 if(p.name != nil){ 1125 free(beam->name); 1126 beam->name = p.name; 1127 } 1128 if(p.equalise != BoolX) 1129 beam->equalise = p.equalise == BoolT; 1130 1131 tkpackqit(master); 1132 tkrunpack(t); 1133 1134 Error: 1135 tkfreename(names); 1136 return e; 1137 } 1138 1139 char* 1140 tkgridsize(TkTop *t, char *arg, char **val, char *buf, char *ebuf) 1141 { 1142 Tk *master; 1143 TkGrid *grid; 1144 char *e; 1145 1146 e = tkgetgridmaster(t, &arg, buf, ebuf, &master); 1147 if(e != nil) 1148 return e; 1149 grid = master->grid; 1150 if(grid == nil) 1151 return tkvalue(val, "0 0"); 1152 else 1153 return tkvalue(val, "%d %d", grid->dim.x, grid->dim.y); 1154 } 1155 1156 char* 1157 tkgridbbox(TkTop *t, char *arg, char **val, char *buf, char *ebuf) 1158 { 1159 Point p0, p1; 1160 Tk *master; 1161 TkGrid *grid; 1162 char *e; 1163 int gotarg; 1164 Point dim; 1165 Rectangle r; 1166 1167 e = tkgetgridmaster(t, &arg, buf, ebuf, &master); 1168 if(e != nil || master->grid == nil) 1169 return e; 1170 1171 grid = master->grid; 1172 dim = grid->dim; 1173 arg = tkword(t, arg, buf, ebuf, &gotarg); 1174 if(!gotarg){ 1175 p0 = ZP; 1176 p1 = dim; 1177 } else{ 1178 p0.x = parsegridindex(grid->cols, dim.x, buf); 1179 arg = tkword(t, arg, buf, ebuf, &gotarg); 1180 if(!gotarg) 1181 return TkFewpt; 1182 p0.y = parsegridindex(grid->rows, dim.y, buf); 1183 arg = tkword(t, arg, buf, ebuf, &gotarg); 1184 if(!gotarg){ 1185 p1 = p0; 1186 } else{ 1187 p1.x = parsegridindex(grid->cols, dim.x, buf); 1188 arg = tkword(t, arg, buf, ebuf, &gotarg); 1189 if(!gotarg) 1190 return TkFewpt; 1191 p1.y = parsegridindex(grid->rows, dim.y, buf); 1192 } 1193 } 1194 if(p0.x < 0 || p0.y < 0 || p1.x < 0 || p1.y < 0) 1195 return TkBadix; 1196 1197 r = cellbbox(grid, p0); 1198 if(!eqpt(p0, p1)) 1199 combinerect(&r, cellbbox(grid, p1)); 1200 return tkvalue(val, "%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y); 1201 } 1202 1203 char* 1204 tkgridindex(TkTop *t, char *arg, char **val, char *buf, char *ebuf, int isrow) 1205 { 1206 Tk *master; 1207 TkGrid *grid; 1208 TkGridbeam *beam; 1209 int blen, i; 1210 1211 arg = tkword(t, arg, buf, ebuf, nil); 1212 master = tklook(t, buf, 0); 1213 if(master == nil) 1214 return TkBadwp; 1215 tkword(t, arg, buf, ebuf, nil); 1216 grid = master->grid; 1217 if(grid == nil){ 1218 beam = nil; 1219 blen = 0; 1220 } else if(isrow){ 1221 beam = grid->rows; 1222 blen = grid->dim.y; 1223 } else{ 1224 beam = grid->cols; 1225 blen = grid->dim.x; 1226 } 1227 i = parsegridindex(beam, blen, buf); 1228 if(i < 0) 1229 return TkBadix; 1230 return tkvalue(val, "%d", i); 1231 } 1232 1233 void 1234 tkfreegrid(TkGrid *grid) 1235 { 1236 Point dim; 1237 int i; 1238 dim = grid->dim; 1239 for(i = 0; i < dim.x; i++) 1240 free(grid->cols[i].name); 1241 for(i = 0; i < dim.y; i++) 1242 free(grid->rows[i].name); 1243 for(i = 0; i < dim.y; i++) 1244 free(grid->cells[i]); 1245 free(grid->cells); 1246 free(grid->rows); 1247 free(grid->cols); 1248 free(grid); 1249 } 1250 1251 char* 1252 tkgrid(TkTop *t, char *arg, char **val) 1253 { 1254 TkGridparam *p; 1255 TkOptab tko[2]; 1256 TkName *names; 1257 char *e, *w, *buf; 1258 1259 buf = mallocz(Tkmaxitem, 0); 1260 if(buf == nil) 1261 return TkNomem; 1262 1263 w = tkword(t, arg, buf, buf+Tkmaxitem, nil); 1264 if('a' <= buf[0] && buf[0] <= 'z'){ 1265 if(strcmp(buf, "debug") == 0){ 1266 Tk *tk; 1267 e = tkgetgridmaster(t, &w, buf, buf+Tkmaxitem, &tk); 1268 if(e == nil) 1269 printgrid(tk->grid); 1270 } else 1271 if(strcmp(buf, "forget") == 0) 1272 e = tkgridforget(t, w, buf, buf+Tkmaxitem); 1273 else if(strcmp(buf, "propagate") == 0) 1274 e = tkpropagate(t, w); 1275 else if(strcmp(buf, "slaves") == 0) 1276 e = tkgridslaves(t, w, val, buf, buf+Tkmaxitem); 1277 else if(strcmp(buf, "rowconfigure") == 0) 1278 e = tkbeamconfigure(t, w, 1); 1279 else if(strcmp(buf, "columnconfigure") == 0) 1280 e = tkbeamconfigure(t, w, 0); 1281 else if(strcmp(buf, "rowinsert") == 0) 1282 e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 1); 1283 else if(strcmp(buf, "columninsert") == 0) 1284 e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 0); 1285 else if(strcmp(buf, "size") == 0) 1286 e = tkgridsize(t, w, val, buf, buf+Tkmaxitem); 1287 else if(strcmp(buf, "rowdelete") == 0) 1288 e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 1); 1289 else if(strcmp(buf, "columndelete") == 0) 1290 e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 0); 1291 else if(strcmp(buf, "rowindex") == 0) 1292 e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 1); 1293 else if(strcmp(buf, "columnindex") == 0) 1294 e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 0); 1295 else if(strcmp(buf, "bbox") == 0) 1296 e = tkgridbbox(t, w, val, buf, buf+Tkmaxitem); 1297 else if(strcmp(buf, "location") == 0) 1298 e = tkgridlocation(t, w, val, buf, buf+Tkmaxitem); 1299 else if(strcmp(buf, "cellinfo") == 0) 1300 e = tkgridcellinfo(t, w, val, buf, buf+Tkmaxitem); 1301 else if(strcmp(buf, "info") == 0) 1302 e = tkgridinfo(t, w, val, buf, buf+Tkmaxitem); 1303 else{ 1304 tkerr(t, buf); 1305 e = TkBadcm; 1306 } 1307 } else{ 1308 p = malloc(sizeof(TkGridparam)); 1309 if(p == nil) 1310 return TkNomem; 1311 tko[0].ptr = p; 1312 tko[0].optab = opts; 1313 tko[1].ptr = nil; 1314 1315 p->span.x = 1; 1316 p->span.y = 1; 1317 p->pad.x = p->pad.y = p->ipad.x = p->ipad.y = -1; 1318 p->sticky = -1; 1319 1320 names = nil; 1321 e = tkparse(t, arg, tko, &names); 1322 if(e != nil){ 1323 free(p); 1324 return e; 1325 } 1326 1327 e = tkgridconfigure(t, p, names); 1328 free(p->row); 1329 free(p->col); 1330 free(p); 1331 tkfreename(names); 1332 } 1333 free(buf); 1334 return e; 1335 } 1336 1337 /* 1338 * expand widths of rows/columns according to weight. 1339 * return amount of space still left over. 1340 */ 1341 static int 1342 expandwidths(int x0, int x1, int totwidth, TkGridbeam *cols, int expandzero) 1343 { 1344 int share, x, slack, m, w, equal; 1345 1346 if(x0 >= x1) 1347 return 0; 1348 1349 share = 0; 1350 for(x = x0; x < x1; x++) 1351 share += cols[x].weight; 1352 1353 slack = totwidth - beamsize(cols, x0, x1); 1354 if(slack <= 0) 1355 return 0; 1356 1357 if(share == 0 && expandzero){ 1358 share = x1 - x0; 1359 equal = 1; 1360 } else 1361 equal = 0; 1362 1363 for(x = x0; x < x1 && share > 0 ; x++){ 1364 w = equal ? 1 : cols[x].weight; 1365 m = slack * w / share; 1366 cols[x].act += m; 1367 slack -= m; 1368 share -= w; 1369 } 1370 return slack; 1371 } 1372 1373 static void 1374 gridequalise(TkGridbeam *beam, int blen) 1375 { 1376 int i, max; 1377 1378 max = 0; 1379 for(i = 0; i < blen; i++) 1380 if(beam[i].equalise == BoolT && beam[i].act > max) 1381 max = beam[i].act; 1382 1383 if(max > 0) 1384 for(i = 0; i < blen; i++) 1385 if(beam[i].equalise == BoolT) 1386 beam[i].act = max; 1387 } 1388 1389 /* 1390 * take into account min/max beam sizes. 1391 * max takes precedence 1392 */ 1393 static void 1394 beamminmax(TkGridbeam *beam, int n) 1395 { 1396 TkGridbeam *e; 1397 e = &beam[n]; 1398 for(; beam < e; beam++){ 1399 if(beam->act < beam->minsize) 1400 beam->act = beam->minsize; 1401 if(beam->act > beam->maxsize) 1402 beam->act = beam->maxsize; 1403 } 1404 } 1405 1406 int 1407 tkgridder(Tk *master) 1408 { 1409 TkGrid *grid; 1410 TkGridcell **cells, *cell; 1411 TkGridbeam *rows, *cols; 1412 TkGeom pos; 1413 Point org; 1414 Tk *slave; 1415 int dx, dy, x, y, w, bw2, fpadx, fpady; 1416 Point req; 1417 1418 grid = master->grid; 1419 dx = grid->dim.x; 1420 dy = grid->dim.y; 1421 cells = grid->cells; 1422 rows = grid->rows; 1423 cols = grid->cols; 1424 1425 for(x = 0; x < dx; x++) 1426 cols[x].act = 0; 1427 1428 /* calculate column widths and row heights (ignoring multi-column cells) */ 1429 for(y = 0; y < dy; y++){ 1430 rows[y].act = 0; 1431 for(x = 0; x < dx; x++){ 1432 cell = &cells[y][x]; 1433 if((slave = cell->tk) != nil){ 1434 bw2 = slave->borderwidth * 2; 1435 w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x; 1436 if(cell->span.x == 1 && w > cols[x].act) 1437 cols[x].act = w; 1438 w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y; 1439 if(cell->span.y == 1 && w > rows[y].act) 1440 rows[y].act = w; 1441 } 1442 } 1443 } 1444 1445 beamminmax(rows, dy); 1446 beamminmax(cols, dx); 1447 1448 /* now check that spanning cells fit in their rows/columns */ 1449 for(y = 0; y < dy; y++) 1450 for(x = 0; x < dx; x++){ 1451 cell = &cells[y][x]; 1452 if((slave = cell->tk) != nil){ 1453 bw2 = slave->borderwidth * 2; 1454 if(cell->span.x > 1){ 1455 w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x; 1456 expandwidths(x, x+cell->span.x, w, cols, 1); 1457 } 1458 if(cell->span.y > 1){ 1459 w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y; 1460 expandwidths(y, y+cell->span.y, w, rows, 1); 1461 } 1462 } 1463 } 1464 1465 gridequalise(rows, dy); 1466 gridequalise(cols, dx); 1467 1468 if(dx == 0) 1469 req.x = 0; 1470 else 1471 req.x = beamsize(cols, 0, dx) + cols[0].pad + cols[dx-1].pad; 1472 1473 if(dy == 0) 1474 req.y = 0; 1475 else 1476 req.y = beamsize(rows, 0, dy) + rows[0].pad + rows[dy-1].pad; 1477 1478 if(req.x != master->req.width || req.y != master->req.height) 1479 if((master->flag & Tknoprop) == 0){ 1480 if(master->geom != nil){ 1481 master->geom(master, master->act.x, master->act.y, 1482 req.x, req.y); 1483 } else{ 1484 master->req.width = req.x; 1485 master->req.height = req.y; 1486 tkpackqit(master->master); 1487 } 1488 return 0; 1489 } 1490 org = ZP; 1491 if(dx > 0 && master->act.width > req.x) 1492 org.x = expandwidths(0, dx, 1493 master->act.width - (cols[0].pad + cols[dx-1].pad), 1494 cols, 0) / 2; 1495 if(dy > 0 && master->act.height > req.y) 1496 org.y = expandwidths(0, dy, 1497 master->act.height - (rows[0].pad + rows[dy-1].pad), 1498 rows, 0) / 2; 1499 1500 grid->origin = org; 1501 pos.y = org.y; 1502 fpady = 0; 1503 for(y = 0; y < dy; y++){ 1504 pos.y += maximum(fpady, rows[y].pad); 1505 fpady = rows[y].pad; 1506 1507 pos.x = org.x; 1508 fpadx = 0; 1509 for(x = 0; x < dx; x++){ 1510 cell = &cells[y][x]; 1511 pos.x += maximum(fpadx, cols[x].pad); 1512 fpadx = cols[x].pad; 1513 if((slave = cell->tk) != nil && cell->span.x > 0){ 1514 pos.width = beamsize(cols, x, x + cell->span.x); 1515 pos.height = beamsize(rows, y, y + cell->span.y); 1516 tksetslavereq(slave, pos); 1517 } 1518 pos.x += cols[x].act; 1519 } 1520 pos.y += rows[y].act; 1521 } 1522 1523 master->dirty = tkrect(master, 1); 1524 tkdirty(master); 1525 return 1; 1526 } 1527