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 mywidget.columnspacing), 64 XmRImmediate, 65 (XtPointer) 0 66 }, 67 { 68 gridRowSpacing, 69 gridRowSpacing, 70 XmRDimension, 71 sizeof (Dimension), 72 XtOffsetOf( GridRec, 73 mywidget.rowspacing), 74 XmRImmediate, 75 (XtPointer) 0 76 }, 77 { 78 gridMargin, 79 gridMargin, 80 XmRDimension, 81 sizeof (Dimension), 82 XtOffsetOf( GridRec, 83 mywidget.margin), 84 XmRImmediate, 85 (XtPointer) 0 86 } 87 }; 88 89 ///* 90 static XtResource constraints[] = 91 { 92 { 93 gridColumn, 94 gridColumn, 95 XmRDimension, 96 sizeof (Dimension), 97 XtOffsetOf( GridConstraintRec, 98 grid.x), 99 XmRImmediate, 100 (XtPointer) 0 101 }, 102 { 103 gridRow, 104 gridRow, 105 XmRDimension, 106 sizeof (Dimension), 107 XtOffsetOf( GridConstraintRec, 108 grid.y), 109 XmRImmediate, 110 (XtPointer) 0 111 }, 112 { 113 gridColspan, 114 gridColspan, 115 XmRDimension, 116 sizeof (Dimension), 117 XtOffsetOf( GridConstraintRec, 118 grid.colspan), 119 XmRImmediate, 120 (XtPointer) 0 121 }, 122 { 123 gridRowspan, 124 gridRowspan, 125 XmRDimension, 126 sizeof (Dimension), 127 XtOffsetOf( GridConstraintRec, 128 grid.rowspan), 129 XmRImmediate, 130 (XtPointer) 0 131 }, 132 { 133 gridMarginLeft, 134 gridMarginLeft, 135 XmRDimension, 136 sizeof (Dimension), 137 XtOffsetOf( GridConstraintRec, 138 grid.margin_left), 139 XmRImmediate, 140 (XtPointer) 0 141 }, 142 { 143 gridMarginRight, 144 gridMarginRight, 145 XmRDimension, 146 sizeof (Dimension), 147 XtOffsetOf( GridConstraintRec, 148 grid.margin_right), 149 XmRImmediate, 150 (XtPointer) 0 151 }, 152 { 153 gridMarginTop, 154 gridMarginTop, 155 XmRDimension, 156 sizeof (Dimension), 157 XtOffsetOf( GridConstraintRec, 158 grid.margin_top), 159 XmRImmediate, 160 (XtPointer) 0 161 }, 162 { 163 gridMarginBottom, 164 gridMarginBottom, 165 XmRDimension, 166 sizeof (Dimension), 167 XtOffsetOf( GridConstraintRec, 168 grid.margin_bottom), 169 XmRImmediate, 170 (XtPointer) 0 171 }, 172 { 173 gridHExpand, 174 gridHExpand, 175 XmRBoolean, 176 sizeof (Boolean), 177 XtOffsetOf( GridConstraintRec, 178 grid.hexpand), 179 XmRImmediate, 180 (XtPointer) 0 181 }, 182 { 183 gridVExpand, 184 gridVExpand, 185 XmRBoolean, 186 sizeof (Boolean), 187 XtOffsetOf( GridConstraintRec, 188 grid.vexpand), 189 XmRImmediate, 190 (XtPointer) 0 191 }, 192 { 193 gridHFill, 194 gridHFill, 195 XmRBoolean, 196 sizeof (Boolean), 197 XtOffsetOf( GridConstraintRec, 198 grid.hfill), 199 XmRImmediate, 200 (XtPointer) 0 201 }, 202 { 203 gridVFill, 204 gridVFill, 205 XmRBoolean, 206 sizeof (Boolean), 207 XtOffsetOf( GridConstraintRec, 208 grid.vfill), 209 XmRImmediate, 210 (XtPointer) 0 211 }, 212 { 213 gridMinWidth, 214 gridMinWidth, 215 XmRDimension, 216 sizeof (Dimension), 217 XtOffsetOf( GridConstraintRec, 218 grid.min_width), 219 XmRImmediate, 220 (XtPointer) 0 221 } 222 }; 223 //*/ 224 225 GridClassRec gridClassRec = { 226 // Core Class 227 { 228 //(WidgetClass)&constraintClassRec, // superclass 229 (WidgetClass)&xmManagerClassRec, 230 "Grid", // class_name 231 sizeof(GridRec), // widget_size 232 grid_class_initialize, // class_initialize 233 NULL, // class_part_initialize 234 FALSE, // class_inited 235 (XtInitProc)grid_initialize, // initialize 236 NULL, // initialize_hook 237 grid_realize, // realize 238 actionslist, // actions 239 XtNumber(actionslist), // num_actions 240 resources, // resources 241 XtNumber(resources), // num_resources 242 NULLQUARK, // xrm_class 243 True, // compress_motion 244 True, // compress_exposure 245 True, // compress_enterleave 246 False, // visible_interest 247 (XtWidgetProc)grid_destroy, // destroy 248 (XtWidgetProc)grid_resize, // resize 249 (XtExposeProc)grid_expose, // expose 250 grid_set_values, // set_values 251 NULL, // set_values_hook 252 XtInheritSetValuesAlmost, // set_values_almost 253 NULL, // get_values_hook 254 (XtAcceptFocusProc)grid_acceptfocus, // accept_focus 255 XtVersion, // version 256 NULL, // callback_offsets 257 //NULL, // tm_table 258 defaultTranslations, 259 XtInheritQueryGeometry, // query_geometry 260 NULL, // display_accelerator 261 NULL, // extension 262 }, 263 // Composite Class 264 { 265 GridGeometryManager, /* geometry_manager */ 266 GridChangeManaged, /* change_managed */ 267 XtInheritInsertChild, /* insert_child */ 268 XtInheritDeleteChild, /* delete_child */ 269 NULL, /* extension */ 270 }, 271 // Constraint Class 272 { 273 constraints, /* resources */ 274 XtNumber(constraints), /* num_resources */ 275 sizeof(GridConstraintRec), /* constraint_size */ 276 grid_constraint_init, /* initialize */ 277 NULL, /* destroy */ 278 ConstraintSetValues, /* set_values */ 279 NULL, /* extension */ 280 }, 281 // XmManager Class 282 ///* 283 { 284 XtInheritTranslations, 285 NULL, 286 0, 287 NULL, 288 0, 289 XmInheritParentProcess, 290 NULL 291 }, 292 //*/ 293 // MyWidget Class 294 { 295 0 296 } 297 }; 298 299 WidgetClass gridClass = (WidgetClass)&gridClassRec; 300 301 302 void grid_class_initialize(Widget request, Widget new, ArgList args, Cardinal *num_args) { 303 304 } 305 void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) { 306 MyWidget mn = (MyWidget)new; 307 308 mn->mywidget.max_col = 0; 309 mn->mywidget.max_row = 0; 310 311 } 312 void grid_realize(MyWidget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) { 313 XtMakeResizeRequest((Widget)w, 400, 400, NULL, NULL); 314 (coreClassRec.core_class.realize)((Widget)w, valueMask, attributes); 315 grid_place_children(w); 316 } 317 318 319 void grid_destroy(MyWidget widget) { 320 321 } 322 void grid_resize(MyWidget widget) { 323 grid_place_children(widget); 324 } 325 326 void grid_expose(MyWidget widget, XEvent *event, Region region) { 327 328 } 329 330 331 Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { 332 return False; 333 } 334 335 Boolean grid_acceptfocus(Widget w, Time *t) { 336 337 } 338 339 void grid_getfocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) { 340 341 } 342 343 void grid_loosefocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) { 344 345 } 346 347 348 349 XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply) { 350 GridRec *grid = (GridRec*)XtParent(widget); 351 GridConstraintRec *constraints = widget->core.constraints; 352 //XtVaSetValues(widget, XmNwidth, request->width, XmNheight, request->height, NULL); 353 if((request->request_mode & CWWidth) == CWWidth) { 354 widget->core.width = request->width; 355 constraints->grid.pref_width = request->width; 356 } 357 if((request->request_mode & CWHeight) == CWHeight) { 358 widget->core.height = request->height; 359 constraints->grid.pref_height = request->height; 360 } 361 grid_place_children((MyWidget)XtParent(widget)); 362 return XtGeometryYes; 363 } 364 365 void GridChangeManaged(Widget widget) { 366 367 } 368 369 Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { 370 GridConstraintRec *constraints = neww->core.constraints; 371 MyWidget grid = (MyWidget)XtParent(neww); 372 if(constraints->grid.x > grid->mywidget.max_col) { 373 grid->mywidget.max_col = constraints->grid.x; 374 } 375 if(constraints->grid.y > grid->mywidget.max_row) { 376 grid->mywidget.max_row = constraints->grid.y; 377 } 378 } 379 380 381 void grid_constraint_init( 382 Widget request, 383 Widget neww, 384 ArgList args, 385 Cardinal* num_args 386 ) 387 { 388 GridConstraintRec *constraints = neww->core.constraints; 389 390 MyWidget grid = (MyWidget)XtParent(neww); 391 if(constraints->grid.x > grid->mywidget.max_col) { 392 grid->mywidget.max_col = constraints->grid.x; 393 } 394 if(constraints->grid.y > grid->mywidget.max_row) { 395 grid->mywidget.max_row = constraints->grid.y; 396 } 397 constraints->grid.pref_width = neww->core.width; 398 constraints->grid.pref_height = neww->core.height; 399 } 400 401 void grid_place_children(MyWidget w) { 402 int ncols = w->mywidget.max_col+1; 403 int nrows = w->mywidget.max_row+1; 404 GridDef *cols = calloc(ncols, sizeof(GridDef)); 405 GridDef *rows = calloc(nrows, sizeof(GridDef)); 406 int num_cols_expanding = 0; 407 int num_rows_expanding = 0; 408 int req_width = 0; 409 int req_height = 0; 410 411 //printf("container width: %d\n", (int)w->core.width); 412 413 // calculate the minimum size requirements for all columns and rows 414 // we need to run this 2 times: for widgets without colspan/rowspan first 415 // and then again for colspan/rowspan > 1 416 int span_max = 1; 417 for(int r=0;r<2;r++) { 418 for(int i=0;i<w->composite.num_children;i++) { 419 Widget child = w->composite.children[i]; 420 GridConstraintRec *constraints = child->core.constraints; 421 if(constraints->grid.pref_width == 0) { 422 constraints->grid.pref_width = child->core.width; 423 } 424 if(constraints->grid.pref_height == 0) { 425 constraints->grid.pref_height = child->core.height; 426 } 427 if(constraints->grid.pref_width < constraints->grid.min_width) { 428 constraints->grid.pref_width = constraints->grid.min_width; 429 } 430 431 if(constraints->grid.colspan > span_max || constraints->grid.rowspan > span_max) { 432 continue; 433 } 434 435 int x = constraints->grid.x; 436 int y = constraints->grid.y; 437 // make sure ncols/nrows is correct 438 // errors shouldn't happen, unless someone messes up the grid internals 439 if(x >= ncols) { 440 fprintf(stderr, "Error: widget x out of bounds\n"); 441 continue; 442 } 443 if(y >= nrows) { 444 fprintf(stderr, "Error: widget y out of bounds\n"); 445 continue; 446 } 447 GridDef *col = &cols[x]; 448 GridDef *row = &rows[y]; 449 450 if(constraints->grid.hexpand) { 451 if(constraints->grid.colspan > 1) { 452 // check if any column in the span is expanding 453 // if not, make the last column expanding 454 GridDef *last_col = col; 455 for(int c=x;c<ncols;c++) { 456 last_col = &cols[c]; 457 if(last_col->expand) { 458 break; 459 } 460 } 461 last_col->expand = TRUE; 462 } else { 463 col->expand = TRUE; 464 } 465 } 466 if(constraints->grid.vexpand) { 467 if(constraints->grid.rowspan > 1) { 468 GridDef *last_row = row; 469 for(int c=x;c<nrows;c++) { 470 last_row = &rows[c]; 471 if(last_row->expand) { 472 break; 473 } 474 } 475 last_row->expand = TRUE; 476 } else { 477 row->expand = TRUE; 478 } 479 } 480 481 // column size 482 if(constraints->grid.colspan > 1) { 483 // check size of all columns in span 484 Dimension span_width = col->size; 485 GridDef *last_col = col; 486 for(int s=x+1;s<ncols;s++) { 487 last_col = &cols[s]; 488 span_width = last_col->size; 489 490 } 491 int diff = constraints->grid.pref_width - span_width; 492 if(diff > 0) { 493 last_col->size += diff; 494 } 495 } else if(constraints->grid.pref_width > col->size) { 496 col->size = constraints->grid.pref_width; 497 } 498 // row size 499 if(constraints->grid.rowspan > 1) { 500 Dimension span_height = row->size; 501 GridDef *last_row = row; 502 for(int s=x+1;s<nrows;s++) { 503 last_row = &rows[s]; 504 span_height = last_row->size; 505 506 } 507 int diff = constraints->grid.pref_height - span_height; 508 if(diff > 0) { 509 last_row->size += diff; 510 } 511 } else if(constraints->grid.pref_height > row->size) { 512 row->size = constraints->grid.pref_height; 513 } 514 } 515 span_max = 50000; // not sure if this is unreasonable low or high 516 } 517 518 // calc required size 519 for(int i=0;i<ncols;i++) { 520 if(cols[i].expand) { 521 num_cols_expanding++; 522 } 523 req_width += cols[i].size; 524 } 525 for(int i=0;i<nrows;i++) { 526 if(rows[i].expand) { 527 num_rows_expanding++; 528 } 529 req_height += rows[i].size; 530 } 531 532 if(req_width > 0 && req_height > 0) { 533 // add col/row spacing 534 req_width += (ncols-1)*w->mywidget.columnspacing; 535 req_height += (nrows-1)*w->mywidget.rowspacing; 536 537 Widget parent = w->core.parent; 538 Dimension rwidth = req_width; 539 Dimension rheight = req_height; 540 if(rwidth < w->core.width) { 541 //rwidth = w->core.width; 542 } 543 if(rheight < w->core.height) { 544 //rheight = w->core.height; 545 } 546 547 if(!w->mywidget.sizerequest) { 548 Dimension actual_width, actual_height; 549 w->mywidget.sizerequest = TRUE; 550 551 //printf("sizerequest: %d x %d\n", (int)req_width, (int)req_height); 552 553 //XtWidgetGeometry request; 554 //request.width = req_width; 555 //request.request_mode = CWWidth; 556 //XtWidgetGeometry reply; 557 //XtGeometryResult result = XtMakeGeometryRequest((Widget)w, &request, &reply); 558 559 XtMakeResizeRequest((Widget)w, req_width, req_height, &actual_width, &actual_height); 560 w->mywidget.sizerequest = FALSE; 561 //printf("size request: %d %d\n", (int)actual_width, (int)actual_height); 562 } 563 564 565 566 } 567 568 // how much space can we add to each expanding col/row 569 int hexpand = 0; 570 int width_diff = (int)w->core.width - req_width; 571 int hexpand2 = 0; 572 if(width_diff > 0 && num_cols_expanding > 0) { 573 hexpand = width_diff / num_cols_expanding; 574 hexpand2 = width_diff-hexpand*num_cols_expanding; 575 } 576 int x = 0; 577 for(int i=0;i<ncols;i++) { 578 cols[i].pos = x; 579 if(cols[i].expand) { 580 cols[i].size += hexpand + hexpand2; 581 } 582 x += cols[i].size + w->mywidget.columnspacing; 583 584 hexpand2 = 0; 585 } 586 587 int vexpand = 0; 588 int height_diff = (int)w->core.height - req_height; 589 int vexpand2 = 0; 590 if(height_diff > 0 && num_rows_expanding > 0) { 591 vexpand = height_diff / num_rows_expanding; 592 vexpand2 = height_diff-vexpand*num_rows_expanding; 593 } 594 int y = 0; 595 for(int i=0;i<nrows;i++) { 596 rows[i].pos = y; 597 if(rows[i].expand) { 598 rows[i].size += vexpand + vexpand2; 599 } 600 y += rows[i].size += w->mywidget.rowspacing; 601 602 vexpand2 = 0; 603 } 604 605 for(int i=0;i<w->composite.num_children;i++) { 606 Widget child = w->composite.children[i]; 607 GridConstraintRec *constraints = child->core.constraints; 608 GridDef c = cols[constraints->grid.x]; 609 GridDef r = rows[constraints->grid.y]; 610 int x = c.pos; 611 int y = r.pos; 612 int width = constraints->grid.pref_width; 613 int height = constraints->grid.pref_height; 614 if(constraints->grid.hfill) { 615 if(constraints->grid.colspan > 1) { 616 Dimension cwidth = 0; 617 for(int j=0;j<constraints->grid.colspan;j++) { 618 if(constraints->grid.x+j < ncols) { 619 cwidth += cols[constraints->grid.x+j].size + (j > 0 ? w->mywidget.columnspacing : 0); 620 } 621 } 622 width = cwidth; 623 } else { 624 width = c.size - w->mywidget.columnspacing; 625 } 626 } 627 if(constraints->grid.vfill) { 628 if(constraints->grid.rowspan > 1) { 629 Dimension cheight = 0; 630 for(int j=0;j<constraints->grid.rowspan;j++) { 631 if(constraints->grid.y+j < nrows) { 632 cheight += rows[constraints->grid.y+j].size + (j > 0 ? w->mywidget.rowspacing : 0); 633 } 634 } 635 height = cheight; 636 } else { 637 height = r.size - w->mywidget.rowspacing; 638 } 639 } 640 641 if(width > 0 && height > 0) { 642 XtConfigureWidget(child, x, y, width, height, child->core.border_width); 643 } 644 //printf("child %d %d - %d %d\n", (int)child->core.x, (int)child->core.y, (int)child->core.width, (int)child->core.height); 645 } 646 647 free(cols); 648 free(rows); 649 } 650 651