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