ui/motif/Grid.c

changeset 431
bb7da585debc
parent 429
0921f8a5d535
child 433
605bb5dc34f1
equal deleted inserted replaced
169:fe49cff3c571 431:bb7da585debc
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 };
214 //*/
215
216 GridClassRec gridClassRec = {
217 // Core Class
218 {
219 //(WidgetClass)&constraintClassRec, // superclass
220 (WidgetClass)&xmManagerClassRec,
221 "Grid", // class_name
222 sizeof(GridRec), // widget_size
223 grid_class_initialize, // class_initialize
224 NULL, // class_part_initialize
225 FALSE, // class_inited
226 (XtInitProc)grid_initialize, // initialize
227 NULL, // initialize_hook
228 grid_realize, // realize
229 actionslist, // actions
230 XtNumber(actionslist), // num_actions
231 resources, // resources
232 XtNumber(resources), // num_resources
233 NULLQUARK, // xrm_class
234 True, // compress_motion
235 True, // compress_exposure
236 True, // compress_enterleave
237 False, // visible_interest
238 (XtWidgetProc)grid_destroy, // destroy
239 (XtWidgetProc)grid_resize, // resize
240 (XtExposeProc)grid_expose, // expose
241 grid_set_values, // set_values
242 NULL, // set_values_hook
243 XtInheritSetValuesAlmost, // set_values_almost
244 NULL, // get_values_hook
245 (XtAcceptFocusProc)grid_acceptfocus, // accept_focus
246 XtVersion, // version
247 NULL, // callback_offsets
248 //NULL, // tm_table
249 defaultTranslations,
250 XtInheritQueryGeometry, // query_geometry
251 NULL, // display_accelerator
252 NULL, // extension
253 },
254 // Composite Class
255 {
256 GridGeometryManager, /* geometry_manager */
257 GridChangeManaged, /* change_managed */
258 XtInheritInsertChild, /* insert_child */
259 XtInheritDeleteChild, /* delete_child */
260 NULL, /* extension */
261 },
262 // Constraint Class
263 {
264 constraints, /* resources */
265 XtNumber(constraints), /* num_resources */
266 sizeof(GridConstraintRec), /* constraint_size */
267 grid_constraint_init, /* initialize */
268 NULL, /* destroy */
269 ConstraintSetValues, /* set_values */
270 NULL, /* extension */
271 },
272 // XmManager Class
273 ///*
274 {
275 XtInheritTranslations,
276 NULL,
277 0,
278 NULL,
279 0,
280 XmInheritParentProcess,
281 NULL
282 },
283 //*/
284 // MyWidget Class
285 {
286 0
287 }
288 };
289
290 WidgetClass gridClass = (WidgetClass)&gridClassRec;
291
292
293 void grid_class_initialize(Widget request, Widget new, ArgList args, Cardinal *num_args) {
294
295 }
296 void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) {
297 MyWidget mn = (MyWidget)new;
298
299 mn->mywidget.max_col = 0;
300 mn->mywidget.max_row = 0;
301
302 }
303 void grid_realize(MyWidget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) {
304 XtMakeResizeRequest((Widget)w, 400, 400, NULL, NULL);
305 (coreClassRec.core_class.realize)((Widget)w, valueMask, attributes);
306 grid_place_children(w);
307 }
308
309
310 void grid_destroy(MyWidget widget) {
311
312 }
313 void grid_resize(MyWidget widget) {
314 grid_place_children(widget);
315 }
316
317 void grid_expose(MyWidget widget, XEvent *event, Region region) {
318
319 }
320
321
322 Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
323 return False;
324 }
325
326 Boolean grid_acceptfocus(Widget w, Time *t) {
327
328 }
329
330 void grid_getfocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) {
331
332 }
333
334 void grid_loosefocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) {
335
336 }
337
338
339
340 XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply) {
341 GridRec *grid = (GridRec*)XtParent(widget);
342 GridConstraintRec *constraints = widget->core.constraints;
343 //XtVaSetValues(widget, XmNwidth, request->width, XmNheight, request->height, NULL);
344 if((request->request_mode & CWWidth) == CWWidth) {
345 widget->core.width = request->width;
346 constraints->grid.pref_width = request->width;
347 }
348 if((request->request_mode & CWHeight) == CWHeight) {
349 widget->core.height = request->height;
350 constraints->grid.pref_height = request->height;
351 }
352 grid_place_children((MyWidget)XtParent(widget));
353 return XtGeometryYes;
354 }
355
356 void GridChangeManaged(Widget widget) {
357
358 }
359
360 Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
361 GridConstraintRec *constraints = neww->core.constraints;
362 MyWidget grid = (MyWidget)XtParent(neww);
363 if(constraints->grid.x > grid->mywidget.max_col) {
364 grid->mywidget.max_col = constraints->grid.x;
365 }
366 if(constraints->grid.y > grid->mywidget.max_row) {
367 grid->mywidget.max_row = constraints->grid.y;
368 }
369 }
370
371
372 void grid_constraint_init(
373 Widget request,
374 Widget neww,
375 ArgList args,
376 Cardinal* num_args
377 )
378 {
379 GridConstraintRec *constraints = neww->core.constraints;
380
381 MyWidget grid = (MyWidget)XtParent(neww);
382 if(constraints->grid.x > grid->mywidget.max_col) {
383 grid->mywidget.max_col = constraints->grid.x;
384 }
385 if(constraints->grid.y > grid->mywidget.max_row) {
386 grid->mywidget.max_row = constraints->grid.y;
387 }
388 constraints->grid.pref_width = neww->core.width;
389 constraints->grid.pref_height = neww->core.height;
390 }
391
392 void grid_place_children(MyWidget w) {
393 int ncols = w->mywidget.max_col+1;
394 int nrows = w->mywidget.max_row+1;
395 GridDef *cols = calloc(ncols, sizeof(GridDef));
396 GridDef *rows = calloc(nrows, sizeof(GridDef));
397 int num_cols_expanding = 0;
398 int num_rows_expanding = 0;
399 int req_width = 0;
400 int req_height = 0;
401
402 //printf("container width: %d\n", (int)w->core.width);
403
404 // calculate the minimum size requirements for all columns and rows
405 // we need to run this 2 times: for widgets without colspan/rowspan first
406 // and then again for colspan/rowspan > 1
407 int span_max = 1;
408 for(int r=0;r<2;r++) {
409 for(int i=0;i<w->composite.num_children;i++) {
410 Widget child = w->composite.children[i];
411 GridConstraintRec *constraints = child->core.constraints;
412 if(constraints->grid.pref_width == 0) {
413 constraints->grid.pref_width = child->core.width;
414 }
415 if(constraints->grid.pref_height == 0) {
416 constraints->grid.pref_height = child->core.height;
417 }
418
419 if(constraints->grid.colspan > span_max || constraints->grid.rowspan > span_max) {
420 continue;
421 }
422
423 int x = constraints->grid.x;
424 int y = constraints->grid.y;
425 // make sure ncols/nrows is correct
426 // errors shouldn't happen, unless someone messes up the grid internals
427 if(x >= ncols) {
428 fprintf(stderr, "Error: widget x out of bounds\n");
429 continue;
430 }
431 if(y >= nrows) {
432 fprintf(stderr, "Error: widget y out of bounds\n");
433 continue;
434 }
435 GridDef *col = &cols[x];
436 GridDef *row = &rows[y];
437
438 if(constraints->grid.hexpand) {
439 if(constraints->grid.colspan > 1) {
440 // check if any column in the span is expanding
441 // if not, make the last column expanding
442 GridDef *last_col = col;
443 for(int c=x;c<ncols;c++) {
444 last_col = &cols[c];
445 if(last_col->expand) {
446 break;
447 }
448 }
449 last_col->expand = TRUE;
450 } else {
451 col->expand = TRUE;
452 }
453 }
454 if(constraints->grid.vexpand) {
455 if(constraints->grid.rowspan > 1) {
456 GridDef *last_row = row;
457 for(int c=x;c<nrows;c++) {
458 last_row = &rows[c];
459 if(last_row->expand) {
460 break;
461 }
462 }
463 last_row->expand = TRUE;
464 } else {
465 row->expand = TRUE;
466 }
467 }
468
469 // column size
470 if(constraints->grid.colspan > 1) {
471 // check size of all columns in span
472 Dimension span_width = col->size;
473 GridDef *last_col = col;
474 for(int s=x+1;s<ncols;s++) {
475 last_col = &cols[s];
476 span_width = last_col->size;
477
478 }
479 int diff = constraints->grid.pref_width - span_width;
480 if(diff > 0) {
481 last_col->size += diff;
482 }
483 } else if(constraints->grid.pref_width > col->size) {
484 col->size = constraints->grid.pref_width;
485 }
486 // row size
487 if(constraints->grid.rowspan > 1) {
488 Dimension span_height = row->size;
489 GridDef *last_row = row;
490 for(int s=x+1;s<nrows;s++) {
491 last_row = &rows[s];
492 span_height = last_row->size;
493
494 }
495 int diff = constraints->grid.pref_height - span_height;
496 if(diff > 0) {
497 last_row->size += diff;
498 }
499 } else if(constraints->grid.pref_height > row->size) {
500 row->size = constraints->grid.pref_height;
501 }
502 }
503 span_max = 50000; // not sure if this is unreasonable low or high
504 }
505
506 // calc required size
507 for(int i=0;i<ncols;i++) {
508 if(cols[i].expand) {
509 num_cols_expanding++;
510 }
511 req_width += cols[i].size;
512 }
513 for(int i=0;i<nrows;i++) {
514 if(rows[i].expand) {
515 num_rows_expanding++;
516 }
517 req_height += rows[i].size;
518 }
519
520 if(req_width > 0 && req_height > 0) {
521 // add col/row spacing
522 req_width += (ncols-1)*w->mywidget.columnspacing;
523 req_height += (nrows-1)*w->mywidget.rowspacing;
524
525 Widget parent = w->core.parent;
526 Dimension rwidth = req_width;
527 Dimension rheight = req_height;
528 if(rwidth < w->core.width) {
529 //rwidth = w->core.width;
530 }
531 if(rheight < w->core.height) {
532 //rheight = w->core.height;
533 }
534
535 if(!w->mywidget.sizerequest) {
536 Dimension actual_width, actual_height;
537 w->mywidget.sizerequest = TRUE;
538
539 //XtWidgetGeometry request;
540 //request.width = req_width;
541 //request.request_mode = CWWidth;
542 //XtWidgetGeometry reply;
543 //XtGeometryResult result = XtMakeGeometryRequest((Widget)w, &request, &reply);
544
545 XtMakeResizeRequest((Widget)w, req_width, req_height, &actual_width, &actual_height);
546 w->mywidget.sizerequest = FALSE;
547 //printf("size request: %d %d\n", (int)actual_width, (int)actual_height);
548 }
549
550
551
552 }
553
554 // how much space can we add to each expanding col/row
555 int hexpand = 0;
556 int width_diff = (int)w->core.width - req_width;
557 int hexpand2 = 0;
558 if(width_diff > 0 && num_cols_expanding > 0) {
559 hexpand = width_diff / num_cols_expanding;
560 hexpand2 = width_diff-hexpand*num_cols_expanding;
561 }
562 int x = 0;
563 for(int i=0;i<ncols;i++) {
564 cols[i].pos = x;
565 if(cols[i].expand) {
566 cols[i].size += hexpand + hexpand2;
567 }
568 x += cols[i].size + w->mywidget.columnspacing;
569
570 hexpand2 = 0;
571 }
572
573 int vexpand = 0;
574 int height_diff = (int)w->core.height - req_height;
575 int vexpand2 = 0;
576 if(height_diff > 0 && num_rows_expanding > 0) {
577 vexpand = height_diff / num_rows_expanding;
578 vexpand2 = height_diff-vexpand*num_rows_expanding;
579 }
580 int y = 0;
581 for(int i=0;i<nrows;i++) {
582 rows[i].pos = y;
583 if(rows[i].expand) {
584 rows[i].size += vexpand + vexpand2;
585 }
586 y += rows[i].size += w->mywidget.rowspacing;
587
588 vexpand2 = 0;
589 }
590
591 for(int i=0;i<w->composite.num_children;i++) {
592 Widget child = w->composite.children[i];
593 GridConstraintRec *constraints = child->core.constraints;
594 GridDef c = cols[constraints->grid.x];
595 GridDef r = rows[constraints->grid.y];
596 int x = c.pos;
597 int y = r.pos;
598 int width = constraints->grid.pref_width;
599 int height = constraints->grid.pref_height;
600 if(constraints->grid.hfill) {
601 if(constraints->grid.colspan > 1) {
602 Dimension cwidth = 0;
603 for(int j=0;j<constraints->grid.colspan;j++) {
604 if(constraints->grid.x+j < ncols) {
605 cwidth += cols[constraints->grid.x+j].size + (j > 0 ? w->mywidget.columnspacing : 0);
606 }
607 }
608 width = cwidth;
609 } else {
610 width = c.size - w->mywidget.columnspacing;
611 }
612 }
613 if(constraints->grid.vfill) {
614 if(constraints->grid.rowspan > 1) {
615 Dimension cheight = 0;
616 for(int j=0;j<constraints->grid.rowspan;j++) {
617 if(constraints->grid.y+j < nrows) {
618 cheight += rows[constraints->grid.y+j].size + (j > 0 ? w->mywidget.rowspacing : 0);
619 }
620 }
621 height = cheight;
622 } else {
623 height = r.size - w->mywidget.rowspacing;
624 }
625 }
626
627 if(width > 0 && height > 0) {
628 XtConfigureWidget(child, x, y, width, height, child->core.border_width);
629 }
630 //printf("child %d %d - %d %d\n", (int)child->core.x, (int)child->core.y, (int)child->core.width, (int)child->core.height);
631 }
632
633 free(cols);
634 free(rows);
635 }
636

mercurial