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