1 #include "lib9.h" 2 #include "draw.h" 3 #include "tk.h" 4 5 #define O(t, e) ((long)(&((t*)0)->e)) 6 7 typedef struct Pack Pack; 8 struct Pack 9 { 10 Tk* t; 11 Pack* next; 12 }; 13 static Pack *packorder; 14 15 static int tkpacker(Tk *); 16 17 typedef struct TkParam TkParam; 18 struct TkParam 19 { 20 Point pad; 21 Point ipad; 22 int side; 23 int anchor; 24 int fill; 25 Tk* in; 26 Tk* before; 27 Tk* after; 28 int expand; 29 }; 30 31 TkParam defparam = { 32 {-1, -1}, /* p.pad */ 33 {-1, -1}, /* p.ipad */ 34 -1, /* side */ 35 -1, /* anchor */ 36 -1, /* fill */ 37 nil, /* in */ 38 nil, /* before */ 39 nil, /* after */ 40 BoolX /* expand */ 41 }; 42 43 static 44 TkStab tkside[] = 45 { 46 "top", Tktop, 47 "bottom", Tkbottom, 48 "left", Tkleft, 49 "right", Tkright, 50 nil 51 }; 52 53 static 54 TkStab tkfill[] = 55 { 56 "none", 0, 57 "x", Tkfillx, 58 "y", Tkfilly, 59 "both", Tkfillx|Tkfilly, 60 nil 61 }; 62 63 static 64 TkOption opts[] = 65 { 66 "padx", OPTnndist, O(TkParam, pad.x), nil, 67 "pady", OPTnndist, O(TkParam, pad.y), nil, 68 "ipadx", OPTnndist, O(TkParam, ipad.x), nil, 69 "ipady", OPTnndist, O(TkParam, ipad.y), nil, 70 "side", OPTstab, O(TkParam, side), tkside, 71 "anchor", OPTstab, O(TkParam, anchor), tkanchor, 72 "fill", OPTstab, O(TkParam, fill), tkfill, 73 "in", OPTwinp, O(TkParam, in), nil, 74 "before", OPTwinp, O(TkParam, before), nil, 75 "after", OPTwinp, O(TkParam, after), nil, 76 "expand", OPTstab, O(TkParam, expand), tkbool, 77 nil 78 }; 79 80 void 81 tkdelpack(Tk *t) 82 { 83 Tk *f, **l, *sub, *p; 84 85 sub = tkfindsub(t); 86 if(sub != nil) { 87 p = sub->parent; 88 if(tkmethod[p->type]->forgetsub != nil) 89 tkmethod[p->type]->forgetsub(sub, t); 90 } 91 92 if(t->master == nil) 93 return; 94 95 if(t->master->grid != nil) 96 tkgriddelslave(t); 97 98 l = &t->master->slave; 99 for(f = *l; f; f = f->next) { 100 if(f == t) { 101 *l = t->next; 102 break; 103 } 104 l = &f->next; 105 } 106 t->master = nil; 107 } 108 109 void 110 tkappendpack(Tk *parent, Tk *tk, int where) 111 { 112 Tk *f, **l; 113 114 tk->master = parent; 115 l = &parent->slave; 116 for(f = *l; f; f = f->next) { 117 if(where-- == 0) 118 break; 119 l = &f->next; 120 } 121 *l = tk; 122 tk->next = f; 123 124 for( ; parent != nil; parent = parent->master) 125 if(parent->parent != nil){ 126 tk->flag |= Tksubsub; 127 break; 128 } 129 } 130 131 static void 132 tkpackqrm(Tk *t) 133 { 134 Pack *f, **l; 135 136 l = &packorder; 137 for(f = *l; f; f = f->next) { 138 if(f->t == t) { 139 *l = f->next; 140 free(f); 141 break; 142 } 143 l = &f->next; 144 } 145 } 146 147 /* XXX - Tad: leaky... should propagate */ 148 void 149 tkpackqit(Tk *t) 150 { 151 Pack *f; 152 153 if(t == nil || (t->flag & Tkdestroy)) 154 return; 155 156 tkpackqrm(t); 157 f = malloc(sizeof(Pack)); 158 if(f == nil) { 159 print("tkpackqit: malloc failed\n"); 160 return; 161 } 162 163 f->t = t; 164 f->next = packorder; 165 packorder = f; 166 } 167 168 void 169 tkrunpack(TkTop *t) 170 { 171 Tk *tk; 172 int done; 173 174 while(packorder != nil) { 175 tk = packorder->t; 176 if (tk->grid != nil) 177 done = tkgridder(tk); 178 else 179 done = tkpacker(tk); 180 if (done) 181 tkpackqrm(tk); 182 } 183 tkenterleave(t); 184 tkdirtyfocusorder(t); 185 } 186 187 static void 188 tksetopt(TkParam *p, Tk *tk) 189 { 190 if(p->pad.x != -1) 191 tk->pad.x = p->pad.x*2; 192 if(p->pad.y != -1) 193 tk->pad.y = p->pad.y*2; 194 if(p->ipad.x != -1) 195 tk->ipad.x = p->ipad.x*2; 196 if(p->ipad.y != -1) 197 tk->ipad.y = p->ipad.y*2; 198 if(p->side != -1) { 199 tk->flag &= ~Tkside; 200 tk->flag |= p->side; 201 } 202 if(p->anchor != -1) { 203 tk->flag &= ~Tkanchor; 204 tk->flag |= p->anchor; 205 } 206 if(p->fill != -1) { 207 tk->flag &= ~Tkfill; 208 tk->flag |= p->fill; 209 } 210 if(p->expand != BoolX) { 211 if(p->expand == BoolT) { 212 tk->flag |= Tkexpand; 213 } 214 else 215 tk->flag &= ~Tkexpand; 216 } 217 } 218 219 static char* 220 tkforget(TkTop *t, char *arg) 221 { 222 Tk *tk; 223 char *buf; 224 225 buf = mallocz(Tkmaxitem, 0); 226 if(buf == nil) 227 return TkNomem; 228 for(;;) { 229 arg = tkword(t, arg, buf, buf+Tkmaxitem, nil); 230 if(buf[0] == '\0') 231 break; 232 tk = tklook(t, buf, 0); 233 if(tk == nil) { 234 tkrunpack(t); 235 tkerr(t, buf); 236 free(buf); 237 return TkBadwp; 238 } 239 tkpackqit(tk->master); 240 tkdelpack(tk); 241 } 242 free(buf); 243 tkrunpack(t); 244 return nil; 245 } 246 247 char* 248 tkpropagate(TkTop *t, char *arg) 249 { 250 Tk *tk; 251 TkStab *s; 252 char *buf; 253 254 buf = mallocz(Tkmaxitem, 0); 255 if(buf == nil) 256 return TkNomem; 257 arg = tkword(t, arg, buf, buf+Tkmaxitem, nil); 258 tk = tklook(t, buf, 0); 259 if(tk == nil) { 260 tkerr(t, buf); 261 free(buf); 262 return TkBadwp; 263 } 264 265 tkword(t, arg, buf, buf+Tkmaxitem, nil); 266 for(s = tkbool; s->val; s++) { 267 if(strcmp(s->val, buf) == 0) { 268 if(s->con == BoolT) { 269 tk->flag &= ~Tknoprop; 270 tkpackqit(tk); 271 tkrunpack(t); 272 } else 273 tk->flag |= Tknoprop; 274 free(buf); 275 return nil; 276 } 277 } 278 free(buf); 279 return TkBadvl; 280 } 281 282 static char* 283 tkslaves(TkTop *t, char *arg, char **val) 284 { 285 Tk *tk; 286 char *fmt, *e, *buf; 287 288 buf = mallocz(Tkmaxitem, 0); 289 if(buf == nil) 290 return TkNomem; 291 tkword(t, arg, buf, buf+Tkmaxitem, nil); 292 tk = tklook(t, buf, 0); 293 if(tk == nil){ 294 tkerr(t, buf); 295 free(buf); 296 return TkBadwp; 297 } 298 free(buf); 299 300 fmt = "%s"; 301 for(tk = tk->slave; tk; tk = tk->next) { 302 if (tk->name != nil) { 303 e = tkvalue(val, fmt, tk->name->name); 304 if(e != nil) 305 return e; 306 fmt = " %s"; 307 } 308 } 309 310 return nil; 311 } 312 313 int 314 tkisslave(Tk *in, Tk *tk) 315 { 316 if(in == nil) 317 return 0; 318 if(in == tk) 319 return 1; 320 for(tk = tk->slave; tk; tk = tk->next) 321 if(tkisslave(in, tk)) 322 return 1; 323 return 0; 324 } 325 326 static char* 327 tkcanpack(Tk *tk, Tk *parent) 328 { 329 if(tkisslave(parent, tk)) 330 return TkRecur; 331 if (parent->grid != nil) { 332 if (parent->slave != nil) 333 return TkIsgrid; 334 tkfreegrid(parent->grid); 335 parent->grid = nil; 336 } 337 return nil; 338 } 339 340 char* 341 tkpack(TkTop *t, char *arg, char **val) 342 { 343 TkParam param = defparam; 344 TkParam *p = ¶m; 345 TkOptab tko[2]; 346 Tk *tk, **l, *tkp; 347 TkName *names, *n; 348 char *e, *w, *buf; 349 350 buf = mallocz(Tkminitem, 0); 351 if(buf == nil) 352 return TkNomem; 353 354 w = tkword(t, arg, buf, buf+Tkminitem, nil); 355 if(strcmp(buf, "forget") == 0) { 356 e = tkforget(t, w); 357 free(buf); 358 return e; 359 } 360 if(strcmp(buf, "propagate") == 0) { 361 e = tkpropagate(t, w); 362 free(buf); 363 return e; 364 } 365 if(strcmp(buf, "slaves") == 0) { 366 e = tkslaves(t, w, val); 367 free(buf); 368 return e; 369 } 370 free(buf); 371 372 tko[0].ptr = p; 373 tko[0].optab = opts; 374 tko[1].ptr = nil; 375 376 names = nil; 377 e = tkparse(t, arg, tko, &names); 378 if(e != nil) 379 return e; 380 381 if((p->before && p->before->master == nil) || 382 (p->after && p->after->master == nil)) { 383 tkfreename(names); 384 return TkNotpk; 385 } 386 387 for(n = names; n; n = n->link) { 388 tkp = tklook(t, n->name, 0); 389 if(tkp == nil) { 390 tkerr(t, n->name); 391 tkfreename(names); 392 return TkBadwp; 393 } 394 if(tkp->flag & Tkwindow) { 395 tkfreename(names); 396 return TkIstop; 397 } 398 if(tkp->parent != nil) { 399 tkfreename(names); 400 return TkWpack; 401 } 402 n->obj = tkp; 403 } 404 405 e = nil; 406 for(n = names; n; n = n->link) { 407 tk = n->obj; 408 if(tk->master == nil) { 409 tk->pad = ZP; 410 tk->ipad = ZP; 411 tk->flag &= ~(Tkanchor|Tkside|Tkfill|Tkexpand); 412 tk->flag |= Tktop; 413 } 414 if(tk->master != nil) { 415 tkpackqit(tk->master); 416 tkdelpack(tk); 417 } 418 if(p->before == nil && p->after == nil && p->in == nil) { 419 tkp = tklook(t, n->name, 1); 420 if(tkp == nil) { 421 e = TkBadwp; 422 tkerr(t, n->name); 423 goto Error; 424 } 425 e = tkcanpack(tk, tkp); 426 if (e != nil) 427 goto Error; 428 tkappendpack(tkp, tk, -1); 429 } 430 else { 431 if(p->in != nil) { 432 e = tkcanpack(tk, p->in); 433 if(e != nil) 434 goto Error; 435 tkappendpack(p->in, tk, -1); 436 } 437 else 438 if(p->before != nil) { 439 e = tkcanpack(tk, p->before->master); 440 if (e != nil) 441 goto Error; 442 tk->master = p->before->master; 443 l = &tk->master->slave; 444 for(;;) { 445 if(*l == p->before) { 446 tk->next = *l; 447 *l = tk; 448 break; 449 } 450 l = &(*l)->next; 451 } 452 p->before = tk; 453 } 454 else { 455 e = tkcanpack(tk, p->after->master); 456 if (e != nil) 457 goto Error; 458 tk->master = p->after->master; 459 tk->next = p->after->next; 460 p->after->next = tk; 461 p->after = tk; 462 } 463 } 464 tksetopt(p, tk); 465 if (tk->master->flag&Tksubsub) 466 tksetbits(tk, Tksubsub); 467 tkpackqit(tk->master); 468 } 469 470 Error: 471 tkfreename(names); 472 tkrunpack(t); 473 474 return e; 475 } 476 477 void 478 tksetslavereq(Tk *slave, TkGeom frame) 479 { 480 Point border; 481 TkGeom pos, old; 482 int slave2BW; 483 void (*geomfn)(Tk*); 484 485 border.x = slave->pad.x; 486 border.y = slave->pad.y; 487 488 slave2BW = slave->borderwidth * 2; 489 490 pos.width = slave->req.width + slave2BW + slave->ipad.x; 491 if((slave->flag&Tkfillx) || (pos.width > (frame.width - border.x))) 492 pos.width = frame.width - border.x; 493 494 pos.height = slave->req.height + slave2BW + slave->ipad.y; 495 if((slave->flag&Tkfilly) || (pos.height > (frame.height - border.y))) 496 pos.height = frame.height - border.y; 497 498 border.x /= 2; 499 border.y /= 2; 500 501 if(slave->flag & Tknorth) 502 pos.y = frame.y + border.y; 503 else 504 if(slave->flag & Tksouth) 505 pos.y = frame.y + frame.height - pos.height - border.y; 506 else 507 pos.y = frame.y + (frame.height - pos.height)/2; 508 509 if(slave->flag & Tkwest) 510 pos.x = frame.x + border.x; 511 else 512 if(slave->flag & Tkeast) 513 pos.x = frame.x + frame.width - pos.width - border.x; 514 else 515 pos.x = frame.x + (frame.width - pos.width)/2; 516 517 pos.width -= slave2BW; 518 pos.height -= slave2BW; 519 520 if(memcmp(&slave->act, &pos, sizeof(TkGeom)) != 0) { 521 old = slave->act; 522 slave->act = pos; 523 geomfn = tkmethod[slave->type]->geom; 524 if(geomfn != nil) 525 geomfn(slave); 526 if(slave->slave) 527 tkpackqit(slave); 528 tkdeliver(slave, TkConfigure, &old); 529 530 slave->dirty = tkrect(slave, 1); 531 slave->flag |= Tkrefresh; 532 } 533 } 534 static int 535 tkexpandx(Tk* slave, int cavityWidth) 536 { 537 int numExpand, minExpand, curExpand, childWidth; 538 539 minExpand = cavityWidth; 540 numExpand = 0; 541 for( ;slave != nil; slave = slave->next) { 542 childWidth = slave->req.width + slave->borderwidth*2 + 543 slave->pad.x + slave->ipad.x; 544 if(slave->flag & (Tktop|Tkbottom)) { 545 curExpand = (cavityWidth - childWidth)/numExpand; 546 if (curExpand < minExpand) 547 minExpand = curExpand; 548 } 549 else { 550 cavityWidth -= childWidth; 551 if(slave->flag & Tkexpand) 552 numExpand++; 553 } 554 } 555 curExpand = cavityWidth/numExpand; 556 if(curExpand < minExpand) 557 minExpand = curExpand; 558 559 return (minExpand < 0) ? 0 : minExpand; 560 } 561 562 static int 563 tkexpandy(Tk *slave, int cavityHeight) 564 { 565 int numExpand, minExpand, curExpand, childHeight; 566 567 minExpand = cavityHeight; 568 numExpand = 0; 569 for ( ;slave != nil; slave = slave->next) { 570 childHeight = slave->req.height + slave->borderwidth*2 + 571 + slave->pad.y + slave->ipad.y; 572 if(slave->flag & (Tkleft|Tkright)) { 573 curExpand = (cavityHeight - childHeight)/numExpand; 574 if(curExpand < minExpand) 575 minExpand = curExpand; 576 } 577 else { 578 cavityHeight -= childHeight; 579 if(slave->flag & Tkexpand) 580 numExpand++; 581 } 582 } 583 curExpand = cavityHeight/numExpand; 584 if(curExpand < minExpand) 585 minExpand = curExpand; 586 587 return (minExpand < 0) ? 0 : minExpand; 588 } 589 590 static int 591 tkpacker(Tk *master) 592 { 593 Tk *slave; 594 TkGeom frame, cavity, pos; 595 int maxwidth, maxheight, tmp, slave2BW; 596 597 pos.width = 0; 598 pos.height = 0; 599 maxwidth = 0; 600 maxheight = 0; 601 602 master->flag |= Tkrefresh; 603 604 for (slave = master->slave; slave != nil; slave = slave->next) { 605 slave2BW = slave->borderwidth*2; 606 if(slave->flag & (Tktop|Tkbottom)) { 607 tmp = slave->req.width + slave2BW + 608 slave->pad.x + slave->ipad.x + pos.width; 609 if(tmp > maxwidth) 610 maxwidth = tmp; 611 pos.height += slave->req.height + slave2BW + 612 slave->pad.y + slave->ipad.y; 613 } 614 else { 615 tmp = slave->req.height + slave2BW + 616 slave->pad.y + slave->ipad.y + pos.height; 617 if(tmp > maxheight) 618 maxheight = tmp; 619 pos.width += slave->req.width + slave2BW + 620 + slave->pad.x + slave->ipad.x; 621 } 622 } 623 if(pos.width > maxwidth) 624 maxwidth = pos.width; 625 if(pos.height > maxheight) 626 maxheight = pos.height; 627 628 if(maxwidth != master->req.width || maxheight != master->req.height) 629 if((master->flag & Tknoprop) == 0) { 630 if(master->geom != nil) { 631 master->geom(master, master->act.x, master->act.y, 632 maxwidth, maxheight); 633 } else { 634 master->req.width = maxwidth; 635 master->req.height = maxheight; 636 tkpackqit(master->master); 637 } 638 return 0; 639 } 640 641 cavity.x = 0; 642 cavity.y = 0; 643 pos.x = 0; 644 pos.y = 0; 645 cavity.width = master->act.width; 646 cavity.height = master->act.height; 647 648 for(slave = master->slave; slave != nil; slave = slave->next) { 649 slave2BW = slave->borderwidth*2; 650 if(slave->flag & (Tktop|Tkbottom)) { 651 frame.width = cavity.width; 652 frame.height = slave->req.height + slave2BW + 653 slave->pad.y + slave->ipad.y; 654 if(slave->flag & Tkexpand) 655 frame.height += tkexpandy(slave, cavity.height); 656 cavity.height -= frame.height; 657 if(cavity.height < 0) { 658 frame.height += cavity.height; 659 cavity.height = 0; 660 } 661 frame.x = cavity.x; 662 if(slave->flag & Tktop) { 663 frame.y = cavity.y; 664 cavity.y += frame.height; 665 } 666 else 667 frame.y = cavity.y + cavity.height; 668 } 669 else { 670 frame.height = cavity.height; 671 frame.width = slave->req.width + slave2BW + 672 slave->pad.x + slave->ipad.x; 673 if(slave->flag & Tkexpand) 674 frame.width += tkexpandx(slave, cavity.width); 675 cavity.width -= frame.width; 676 if(cavity.width < 0) { 677 frame.width += cavity.width; 678 cavity.width = 0; 679 } 680 frame.y = cavity.y; 681 if(slave->flag & Tkleft) { 682 frame.x = cavity.x; 683 cavity.x += frame.width; 684 } 685 else 686 frame.x = cavity.x + cavity.width; 687 } 688 689 tksetslavereq(slave, frame); 690 } 691 692 master->dirty = tkrect(master, 1); 693 tkdirty(master); 694 return 1; 695 } 696 697