UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * 31 */ 32 33 #define _GNU_SOURCE 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 38 #include "Grid.h" 39 40 #include <X11/Xlib.h> 41 42 43 44 static XtActionsRec actionslist[] = { 45 {"getfocus",grid_getfocus}, 46 {"loosefocus",grid_loosefocus}, 47 {"NULL",NULL} 48 }; 49 50 //static char defaultTranslations[] = "<BtnDown>: mousedown()\n"; 51 static char defaultTranslations[] = "\ 52 <EnterWindow>: getfocus()\n\ 53 <LeaveWindow>: loosefocus()\n"; 54 55 static XtResource resources[] = 56 { 57 { 58 gridColumnSpacing, 59 gridColumnSpacing, 60 XmRDimension, 61 sizeof (Dimension), 62 XtOffsetOf( GridRec, 63 grid.columnspacing), 64 XmRImmediate, 65 (XtPointer) 0 66 }, 67 { 68 gridRowSpacing, 69 gridRowSpacing, 70 XmRDimension, 71 sizeof (Dimension), 72 XtOffsetOf( GridRec, 73 grid.rowspacing), 74 XmRImmediate, 75 (XtPointer) 0 76 }, 77 { 78 gridPaddingLeft, 79 gridPaddingLeft, 80 XmRDimension, 81 sizeof (Dimension), 82 XtOffsetOf( GridRec, 83 grid.padding_left), 84 XmRImmediate, 85 (XtPointer) 0 86 }, 87 { 88 gridPaddingRight, 89 gridPaddingRight, 90 XmRDimension, 91 sizeof (Dimension), 92 XtOffsetOf( GridRec, 93 grid.padding_right), 94 XmRImmediate, 95 (XtPointer) 0 96 }, 97 { 98 gridPaddingTop, 99 gridPaddingTop, 100 XmRDimension, 101 sizeof (Dimension), 102 XtOffsetOf( GridRec, 103 grid.padding_top), 104 XmRImmediate, 105 (XtPointer) 0 106 }, 107 { 108 gridPaddingBottom, 109 gridPaddingBottom, 110 XmRDimension, 111 sizeof (Dimension), 112 XtOffsetOf( GridRec, 113 grid.padding_bottom), 114 XmRImmediate, 115 (XtPointer) 0 116 } 117 }; 118 119 ///* 120 static XtResource constraints[] = 121 { 122 { 123 gridColumn, 124 gridColumn, 125 XmRDimension, 126 sizeof (Dimension), 127 XtOffsetOf( GridConstraintRec, 128 grid.x), 129 XmRImmediate, 130 (XtPointer) 0 131 }, 132 { 133 gridRow, 134 gridRow, 135 XmRDimension, 136 sizeof (Dimension), 137 XtOffsetOf( GridConstraintRec, 138 grid.y), 139 XmRImmediate, 140 (XtPointer) 0 141 }, 142 { 143 gridColspan, 144 gridColspan, 145 XmRDimension, 146 sizeof (Dimension), 147 XtOffsetOf( GridConstraintRec, 148 grid.colspan), 149 XmRImmediate, 150 (XtPointer) 0 151 }, 152 { 153 gridRowspan, 154 gridRowspan, 155 XmRDimension, 156 sizeof (Dimension), 157 XtOffsetOf( GridConstraintRec, 158 grid.rowspan), 159 XmRImmediate, 160 (XtPointer) 0 161 }, 162 { 163 gridMarginLeft, 164 gridMarginLeft, 165 XmRDimension, 166 sizeof (Dimension), 167 XtOffsetOf( GridConstraintRec, 168 grid.margin_left), 169 XmRImmediate, 170 (XtPointer) 0 171 }, 172 { 173 gridMarginRight, 174 gridMarginRight, 175 XmRDimension, 176 sizeof (Dimension), 177 XtOffsetOf( GridConstraintRec, 178 grid.margin_right), 179 XmRImmediate, 180 (XtPointer) 0 181 }, 182 { 183 gridMarginTop, 184 gridMarginTop, 185 XmRDimension, 186 sizeof (Dimension), 187 XtOffsetOf( GridConstraintRec, 188 grid.margin_top), 189 XmRImmediate, 190 (XtPointer) 0 191 }, 192 { 193 gridMarginBottom, 194 gridMarginBottom, 195 XmRDimension, 196 sizeof (Dimension), 197 XtOffsetOf( GridConstraintRec, 198 grid.margin_bottom), 199 XmRImmediate, 200 (XtPointer) 0 201 }, 202 { 203 gridHExpand, 204 gridHExpand, 205 XmRBoolean, 206 sizeof (Boolean), 207 XtOffsetOf( GridConstraintRec, 208 grid.hexpand), 209 XmRImmediate, 210 (XtPointer) 0 211 }, 212 { 213 gridVExpand, 214 gridVExpand, 215 XmRBoolean, 216 sizeof (Boolean), 217 XtOffsetOf( GridConstraintRec, 218 grid.vexpand), 219 XmRImmediate, 220 (XtPointer) 0 221 }, 222 { 223 gridHFill, 224 gridHFill, 225 XmRBoolean, 226 sizeof (Boolean), 227 XtOffsetOf( GridConstraintRec, 228 grid.hfill), 229 XmRImmediate, 230 (XtPointer) 0 231 }, 232 { 233 gridVFill, 234 gridVFill, 235 XmRBoolean, 236 sizeof (Boolean), 237 XtOffsetOf( GridConstraintRec, 238 grid.vfill), 239 XmRImmediate, 240 (XtPointer) 0 241 }, 242 { 243 gridMinWidth, 244 gridMinWidth, 245 XmRDimension, 246 sizeof (Dimension), 247 XtOffsetOf( GridConstraintRec, 248 grid.min_width), 249 XmRImmediate, 250 (XtPointer) 0 251 } 252 }; 253 //*/ 254 255 GridClassRec gridClassRec = { 256 // Core Class 257 { 258 //(WidgetClass)&constraintClassRec, // superclass 259 (WidgetClass)&xmManagerClassRec, 260 "Grid", // class_name 261 sizeof(GridRec), // widget_size 262 grid_class_initialize, // class_initialize 263 NULL, // class_part_initialize 264 FALSE, // class_inited 265 (XtInitProc)grid_initialize, // initialize 266 NULL, // initialize_hook 267 grid_realize, // realize 268 actionslist, // actions 269 XtNumber(actionslist), // num_actions 270 resources, // resources 271 XtNumber(resources), // num_resources 272 NULLQUARK, // xrm_class 273 True, // compress_motion 274 True, // compress_exposure 275 True, // compress_enterleave 276 False, // visible_interest 277 (XtWidgetProc)grid_destroy, // destroy 278 (XtWidgetProc)grid_resize, // resize 279 (XtExposeProc)grid_expose, // expose 280 grid_set_values, // set_values 281 NULL, // set_values_hook 282 XtInheritSetValuesAlmost, // set_values_almost 283 NULL, // get_values_hook 284 (XtAcceptFocusProc)grid_acceptfocus, // accept_focus 285 XtVersion, // version 286 NULL, // callback_offsets 287 //NULL, // tm_table 288 defaultTranslations, 289 XtInheritQueryGeometry, // query_geometry 290 NULL, // display_accelerator 291 NULL, // extension 292 }, 293 // Composite Class 294 { 295 GridGeometryManager, /* geometry_manager */ 296 GridChangeManaged, /* change_managed */ 297 XtInheritInsertChild, /* insert_child */ 298 XtInheritDeleteChild, /* delete_child */ 299 NULL, /* extension */ 300 }, 301 // Constraint Class 302 { 303 constraints, /* resources */ 304 XtNumber(constraints), /* num_resources */ 305 sizeof(GridConstraintRec), /* constraint_size */ 306 grid_constraint_init, /* initialize */ 307 NULL, /* destroy */ 308 ConstraintSetValues, /* set_values */ 309 NULL, /* extension */ 310 }, 311 // XmManager Class 312 ///* 313 { 314 XtInheritTranslations, 315 NULL, 316 0, 317 NULL, 318 0, 319 XmInheritParentProcess, 320 NULL 321 }, 322 //*/ 323 // MyWidget Class 324 { 325 0 326 } 327 }; 328 329 WidgetClass gridClass = (WidgetClass)&gridClassRec; 330 331 332 void grid_class_initialize(void) { 333 334 } 335 void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) { 336 Grid mn = (Grid)new; 337 338 mn->grid.max_col = 0; 339 mn->grid.max_row = 0; 340 341 } 342 void grid_realize(Widget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) { 343 Grid grid = (Grid)w; 344 XtMakeResizeRequest(w, 400, 400, NULL, NULL); 345 (coreClassRec.core_class.realize)((Widget)w, valueMask, attributes); 346 grid_place_children(grid); 347 } 348 349 350 void grid_destroy(Grid widget) { 351 352 } 353 void grid_resize(Grid widget) { 354 grid_place_children(widget); 355 } 356 357 void grid_expose(Grid widget, XEvent *event, Region region) { 358 359 } 360 361 362 Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { 363 return False; 364 } 365 366 Boolean grid_acceptfocus(Widget w, Time *t) { 367 368 } 369 370 void grid_getfocus(Widget myw, XEvent *event, String *params, Cardinal *nparam) { 371 372 } 373 374 void grid_loosefocus(Widget myw, XEvent *event, String *params, Cardinal *nparam) { 375 376 } 377 378 379 380 XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply) { 381 GridRec *grid = (GridRec*)XtParent(widget); 382 GridConstraintRec *constraints = widget->core.constraints; 383 //XtVaSetValues(widget, XmNwidth, request->width, XmNheight, request->height, NULL); 384 if((request->request_mode & CWWidth) == CWWidth) { 385 widget->core.width = request->width; 386 constraints->grid.pref_width = request->width; 387 } 388 if((request->request_mode & CWHeight) == CWHeight) { 389 widget->core.height = request->height; 390 constraints->grid.pref_height = request->height; 391 } 392 grid_place_children((Grid)XtParent(widget)); 393 return XtGeometryYes; 394 } 395 396 void GridChangeManaged(Widget widget) { 397 grid_place_children((Grid)widget); 398 } 399 400 Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { 401 GridConstraintRec *constraints = neww->core.constraints; 402 Grid grid = (Grid)XtParent(neww); 403 if(constraints->grid.x > grid->grid.max_col) { 404 grid->grid.max_col = constraints->grid.x; 405 } 406 if(constraints->grid.y > grid->grid.max_row) { 407 grid->grid.max_row = constraints->grid.y; 408 } 409 } 410 411 412 void grid_constraint_init( 413 Widget request, 414 Widget neww, 415 ArgList args, 416 Cardinal* num_args 417 ) 418 { 419 GridConstraintRec *constraints = neww->core.constraints; 420 421 Grid grid = (Grid)XtParent(neww); 422 if(constraints->grid.x > grid->grid.max_col) { 423 grid->grid.max_col = constraints->grid.x; 424 } 425 if(constraints->grid.y > grid->grid.max_row) { 426 grid->grid.max_row = constraints->grid.y; 427 } 428 constraints->grid.pref_width = neww->core.width; 429 constraints->grid.pref_height = neww->core.height; 430 } 431 432 void grid_place_children(Grid w) { 433 if(!XtIsRealized((Widget)w)) { 434 return; 435 } 436 437 int ncols = w->grid.max_col+1; 438 int nrows = w->grid.max_row+1; 439 GridDef *cols = calloc(ncols, sizeof(GridDef)); 440 GridDef *rows = calloc(nrows, sizeof(GridDef)); 441 int num_cols_expanding = 0; 442 int num_rows_expanding = 0; 443 int req_width = w->grid.padding_left + w->grid.padding_right; 444 int req_height = w->grid.padding_top + w->grid.padding_bottom; 445 int width = w->core.width; 446 int height = w->core.height; 447 448 //printf("container width: %d\n", (int)w->core.width); 449 450 // calculate the minimum size requirements for all columns and rows 451 // we need to run this 2 times: for widgets without colspan/rowspan first 452 // and then again for colspan/rowspan > 1 453 int span_max = 1; 454 for(int r=0;r<2;r++) { 455 for(int i=0;i<w->composite.num_children;i++) { 456 Widget child = w->composite.children[i]; 457 GridConstraintRec *constraints = child->core.constraints; 458 if(constraints->grid.pref_width == 0) { 459 constraints->grid.pref_width = child->core.width; 460 } 461 if(constraints->grid.pref_height == 0) { 462 constraints->grid.pref_height = child->core.height; 463 } 464 if(constraints->grid.pref_width < constraints->grid.min_width) { 465 constraints->grid.pref_width = constraints->grid.min_width; 466 } 467 int elm_width = constraints->grid.pref_width + constraints->grid.margin_left + constraints->grid.margin_right; 468 int elm_height = constraints->grid.pref_height + constraints->grid.margin_top + constraints->grid.margin_bottom; 469 if(!XtIsManaged(child)) { 470 elm_width = 0; 471 elm_height = 0; 472 } 473 474 if(constraints->grid.colspan > span_max || constraints->grid.rowspan > span_max) { 475 continue; 476 } 477 478 int x = constraints->grid.x; 479 int y = constraints->grid.y; 480 // make sure ncols/nrows is correct 481 // errors shouldn't happen, unless someone messes up the grid internals 482 if(x >= ncols) { 483 fprintf(stderr, "Error: widget x out of bounds\n"); 484 continue; 485 } 486 if(y >= nrows) { 487 fprintf(stderr, "Error: widget y out of bounds\n"); 488 continue; 489 } 490 GridDef *col = &cols[x]; 491 GridDef *row = &rows[y]; 492 493 if(constraints->grid.hexpand) { 494 if(constraints->grid.colspan > 1) { 495 // check if any column in the span is expanding 496 // if not, make the last column expanding 497 GridDef *last_col = col; 498 for(int c=x;c<ncols;c++) { 499 last_col = &cols[c]; 500 if(last_col->expand) { 501 break; 502 } 503 } 504 last_col->expand = TRUE; 505 } else { 506 col->expand = TRUE; 507 } 508 } 509 if(constraints->grid.vexpand) { 510 if(constraints->grid.rowspan > 1) { 511 GridDef *last_row = row; 512 for(int c=x;c<nrows;c++) { 513 last_row = &rows[c]; 514 if(last_row->expand) { 515 break; 516 } 517 } 518 last_row->expand = TRUE; 519 } else { 520 row->expand = TRUE; 521 } 522 } 523 524 // column size 525 if(constraints->grid.colspan > 1) { 526 // check size of all columns in span 527 Dimension span_width = col->size; 528 GridDef *last_col = col; 529 for(int s=x+1;s<ncols;s++) { 530 last_col = &cols[s]; 531 span_width = last_col->size; 532 533 } 534 int diff = elm_width - span_width; 535 if(diff > 0) { 536 last_col->size += diff; 537 } 538 } else if(elm_width > col->size) { 539 col->size = elm_width; 540 } 541 // row size 542 if(constraints->grid.rowspan > 1) { 543 Dimension span_height = row->size; 544 GridDef *last_row = row; 545 for(int s=x+1;s<nrows;s++) { 546 last_row = &rows[s]; 547 span_height = last_row->size; 548 549 } 550 int diff = elm_height - span_height; 551 if(diff > 0) { 552 last_row->size += diff; 553 } 554 } else if(elm_height > row->size) { 555 row->size = elm_height; 556 } 557 } 558 span_max = 50000; // not sure if this is unreasonable low or high 559 } 560 561 // calc required size 562 for(int i=0;i<ncols;i++) { 563 if(cols[i].expand) { 564 num_cols_expanding++; 565 } 566 req_width += cols[i].size; 567 } 568 for(int i=0;i<nrows;i++) { 569 if(rows[i].expand) { 570 num_rows_expanding++; 571 } 572 req_height += rows[i].size; 573 } 574 575 int total_colspacing = 0; 576 int total_rowspacing = 0; 577 for(int i=0;i+1<ncols;i++) { 578 if(cols[i].size > 0) { 579 total_colspacing += w->grid.columnspacing; 580 } 581 } 582 for(int i=0;i+1<nrows;i++) { 583 if(rows[i].size > 0) { 584 total_rowspacing += w->grid.rowspacing; 585 } 586 } 587 588 if(req_width > 0 && req_height > 0) { 589 // add col/row spacing 590 req_width += total_colspacing; //(ncols-1)*w->grid.columnspacing; 591 req_height += total_rowspacing; //(nrows-1)*w->grid.rowspacing; 592 593 Widget parent = w->core.parent; 594 Dimension rwidth = req_width; 595 Dimension rheight = req_height; 596 if(rwidth < w->core.width) { 597 //rwidth = w->core.width; 598 } 599 if(rheight < w->core.height) { 600 //rheight = w->core.height; 601 } 602 603 if(!w->grid.sizerequest) { 604 Dimension actual_width, actual_height; 605 w->grid.sizerequest = TRUE; 606 607 //printf("sizerequest: %d x %d\n", (int)req_width, (int)req_height); 608 609 //XtWidgetGeometry request; 610 //request.width = req_width; 611 //request.request_mode = CWWidth; 612 //XtWidgetGeometry reply; 613 //XtGeometryResult result = XtMakeGeometryRequest((Widget)w, &request, &reply); 614 615 XtMakeResizeRequest((Widget)w, req_width, req_height, &actual_width, &actual_height); 616 w->grid.sizerequest = FALSE; 617 //printf("size request: %d %d\n", (int)actual_width, (int)actual_height); 618 } 619 620 621 622 } 623 624 // how much space can we add to each expanding col/row 625 int hexpand = 0; 626 int width_diff = width - req_width; 627 int hexpand2 = 0; 628 if(width_diff > 0 && num_cols_expanding > 0) { 629 hexpand = width_diff / num_cols_expanding; 630 hexpand2 = width_diff-hexpand*num_cols_expanding; 631 } 632 int x = w->grid.padding_left; 633 for(int i=0;i<ncols;i++) { 634 cols[i].pos = x; 635 if(cols[i].expand) { 636 cols[i].size += hexpand + hexpand2; 637 } 638 if(cols[i].size > 0) { 639 x += cols[i].size + w->grid.columnspacing; 640 } 641 642 hexpand2 = 0; 643 } 644 645 int vexpand = 0; 646 int height_diff = height - req_height; 647 int vexpand2 = 0; 648 if(height_diff > 0 && num_rows_expanding > 0) { 649 vexpand = height_diff / num_rows_expanding; 650 vexpand2 = height_diff-vexpand*num_rows_expanding; 651 } 652 int y = w->grid.padding_bottom; 653 for(int i=0;i<nrows;i++) { 654 rows[i].pos = y; 655 if(rows[i].expand) { 656 rows[i].size += vexpand + vexpand2; 657 } 658 if(rows[i].size > 0) { 659 y += rows[i].size += w->grid.rowspacing; 660 } 661 662 vexpand2 = 0; 663 } 664 665 for(int i=0;i<w->composite.num_children;i++) { 666 Widget child = w->composite.children[i]; 667 GridConstraintRec *constraints = child->core.constraints; 668 GridDef c = cols[constraints->grid.x]; 669 GridDef r = rows[constraints->grid.y]; 670 int x = c.pos + constraints->grid.margin_left; 671 int y = r.pos + constraints->grid.margin_top; 672 int width = constraints->grid.pref_width; 673 int height = constraints->grid.pref_height; 674 if(constraints->grid.hfill) { 675 if(constraints->grid.colspan > 1) { 676 Dimension cwidth = 0; 677 for(int j=0;j<constraints->grid.colspan;j++) { 678 if(constraints->grid.x+j < ncols) { 679 cwidth += cols[constraints->grid.x+j].size + (j > 0 ? w->grid.columnspacing : 0); 680 } 681 } 682 width = cwidth; 683 } else { 684 width = c.size - w->grid.columnspacing - constraints->grid.margin_left - constraints->grid.margin_right; 685 } 686 } 687 if(constraints->grid.vfill) { 688 if(constraints->grid.rowspan > 1) { 689 Dimension cheight = 0; 690 for(int j=0;j<constraints->grid.rowspan;j++) { 691 if(constraints->grid.y+j < nrows) { 692 cheight += rows[constraints->grid.y+j].size + (j > 0 ? w->grid.rowspacing : 0); 693 } 694 } 695 height = cheight; 696 } else { 697 height = r.size - w->grid.rowspacing - constraints->grid.margin_top - constraints->grid.margin_bottom; 698 } 699 } 700 701 if(width > 0 && height > 0) { 702 XtConfigureWidget(child, x, y, width, height, child->core.border_width); 703 } 704 //printf("child %d %d - %d %d\n", (int)child->core.x, (int)child->core.y, (int)child->core.width, (int)child->core.height); 705 } 706 707 free(cols); 708 free(rows); 709 } 710 711