ui/motif/Grid.c

changeset 100
d2bd73d28ff1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/motif/Grid.c	Thu Dec 12 20:01:43 2024 +0100
@@ -0,0 +1,582 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * 
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Grid.h"
+
+#include <X11/Xlib.h>
+
+
+
+static XtActionsRec actionslist[] = {
+  {"getfocus",grid_getfocus},
+  {"loosefocus",grid_loosefocus},
+  {"NULL",NULL}
+};
+
+//static char defaultTranslations[] = "<BtnDown>: mousedown()\n";
+static char defaultTranslations[] = "\
+<EnterWindow>:		getfocus()\n\
+<LeaveWindow>:          loosefocus()\n";
+
+
+///*
+static XtResource constraints[] =
+{
+    {
+        gridColumn,
+        gridColumn,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridConstraintRec,
+                   grid.x),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridRow,
+        gridRow,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridConstraintRec,
+                   grid.y),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridColspan,
+        gridColspan,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridConstraintRec,
+                   grid.colspan),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridRowspan,
+        gridRowspan,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridConstraintRec,
+                   grid.rowspan),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridMarginLeft,
+        gridMarginLeft,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridConstraintRec,
+                   grid.margin_left),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridMarginRight,
+        gridMarginRight,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridConstraintRec,
+                   grid.margin_right),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridMarginTop,
+        gridMarginTop,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridConstraintRec,
+                   grid.margin_top),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridMarginBottom,
+        gridMarginBottom,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridConstraintRec,
+                   grid.margin_bottom),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridHExpand,
+        gridHExpand,
+        XmRBoolean,
+        sizeof (Boolean),
+        XtOffsetOf( GridConstraintRec,
+                   grid.hexpand),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridVExpand,
+        gridVExpand,
+        XmRBoolean,
+        sizeof (Boolean),
+        XtOffsetOf( GridConstraintRec,
+                   grid.vexpand),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridHFill,
+        gridHFill,
+        XmRBoolean,
+        sizeof (Boolean),
+        XtOffsetOf( GridConstraintRec,
+                   grid.hfill),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridVFill,
+        gridVFill,
+        XmRBoolean,
+        sizeof (Boolean),
+        XtOffsetOf( GridConstraintRec,
+                   grid.vfill),
+        XmRImmediate,
+        (XtPointer) 0
+    }
+    
+};
+//*/
+//static XtResource constraints[] = {};
+
+GridClassRec gridClassRec = {
+    // Core Class
+    {
+        //(WidgetClass)&constraintClassRec,   // superclass  
+        (WidgetClass)&xmManagerClassRec,
+        "Grid",                       // class_name
+        sizeof(GridRec),          // widget_size
+        grid_class_initialize,    // class_initialize
+        NULL,                         // class_part_initialize
+        FALSE,                        // class_inited
+        (XtInitProc)grid_initialize,          // initialize
+        NULL,                         // initialize_hook
+        grid_realize,             // realize
+        actionslist,                         // actions
+        XtNumber(actionslist),                            // num_actions
+        NULL,                         // resources
+        0,                            // num_resources
+        NULLQUARK,                    // xrm_class
+        True,                         // compress_motion
+        True,                         // compress_exposure
+        True,                         // compress_enterleave
+        False,                        // visible_interest
+        (XtWidgetProc)grid_destroy,             // destroy
+        (XtWidgetProc)grid_resize,              // resize
+        (XtExposeProc)grid_expose,              // expose
+        grid_set_values,          // set_values
+        NULL,                         // set_values_hook
+        XtInheritSetValuesAlmost,     // set_values_almost
+        NULL,                         // get_values_hook
+        (XtAcceptFocusProc)grid_acceptfocus,       // accept_focus
+        XtVersion,                    // version
+        NULL,                         // callback_offsets
+        //NULL,                         // tm_table
+                defaultTranslations,
+        XtInheritQueryGeometry,       // query_geometry
+        NULL,                         // display_accelerator
+        NULL,                         // extension
+    },
+    // Composite Class
+    {
+        GridGeometryManager, /* geometry_manager */
+        GridChangeManaged,  /* change_managed */   
+        XtInheritInsertChild,  /* insert_child */ 
+        XtInheritDeleteChild,  /* delete_child */  
+        NULL,                 /* extension */    
+    },
+    // Constraint Class
+    {
+        constraints,    /* resources */  
+        XtNumber(constraints),  /* num_resources */    
+        sizeof(GridConstraintRec),  /* constraint_size */  
+        grid_constraint_init,  /* initialize */  
+        NULL,  /* destroy */
+        ConstraintSetValues,  /* set_values */   
+        NULL,  /* extension */    
+    },
+    // XmManager Class
+    ///*
+    {
+        NULL,
+        NULL,
+        0,
+        NULL,
+        0,
+        NULL,
+        NULL
+    },
+    //*/
+    // MyWidget Class
+    {
+        0
+    }
+};
+
+WidgetClass gridClass = (WidgetClass)&gridClassRec;
+
+
+void grid_class_initialize(Widget request, Widget new, ArgList args, Cardinal *num_args) {
+    
+}
+void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) {
+    MyWidget mn = (MyWidget)new;
+    
+    mn->mywidget.max_col = 0;
+    mn->mywidget.max_row = 0;
+    
+}
+void grid_realize(MyWidget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) {
+    XtMakeResizeRequest((Widget)w, 400, 400, NULL, NULL);
+    (coreClassRec.core_class.realize)((Widget)w, valueMask, attributes); 
+    grid_place_children(w);
+}
+
+
+void grid_destroy(MyWidget widget) {
+    
+}
+void grid_resize(MyWidget widget) {
+    grid_place_children(widget);
+}
+
+void grid_expose(MyWidget widget, XEvent *event, Region region) {
+    
+}
+
+
+Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
+    return False;
+}
+
+Boolean grid_acceptfocus(Widget w, Time *t) {
+    
+}
+
+void grid_getfocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) {
+    
+}
+
+void grid_loosefocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) {
+    
+}
+
+
+
+XtGeometryResult GridGeometryManager(Widget	widget, XtWidgetGeometry *request, XtWidgetGeometry *reply) {
+    GridRec *grid = (GridRec*)XtParent(widget);
+    GridConstraintRec *constraints = widget->core.constraints;
+    //XtVaSetValues(widget, XmNwidth, request->width, XmNheight, request->height, NULL);
+    if((request->request_mode & CWWidth) == CWWidth) {
+        widget->core.width = request->width;
+        constraints->grid.pref_width = request->width;
+    }
+    if((request->request_mode & CWHeight) == CWHeight) {
+        widget->core.height = request->height;
+        constraints->grid.pref_height = request->height;
+    }
+    grid_place_children((MyWidget)XtParent(widget));
+    return XtGeometryYes;
+}
+
+void GridChangeManaged(Widget widget) {
+    
+}
+
+Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
+    GridConstraintRec *constraints = neww->core.constraints;
+    MyWidget grid = (MyWidget)XtParent(neww);
+    if(constraints->grid.x > grid->mywidget.max_col) {
+        grid->mywidget.max_col = constraints->grid.x;
+    }
+    if(constraints->grid.y > grid->mywidget.max_row) {
+        grid->mywidget.max_row = constraints->grid.y;
+    }
+}
+
+
+void grid_constraint_init(
+    Widget	request,
+    Widget	neww,
+    ArgList	args,
+    Cardinal*	num_args
+)
+{
+    GridConstraintRec *constraints = neww->core.constraints;
+    
+    MyWidget grid = (MyWidget)XtParent(neww);
+    if(constraints->grid.x > grid->mywidget.max_col) {
+        grid->mywidget.max_col = constraints->grid.x;
+    }
+    if(constraints->grid.y > grid->mywidget.max_row) {
+        grid->mywidget.max_row = constraints->grid.y;
+    }
+    constraints->grid.pref_width = neww->core.width;
+    constraints->grid.pref_height = neww->core.height;
+}
+
+void grid_place_children(MyWidget w) {
+    int ncols = w->mywidget.max_col+1;
+    int nrows = w->mywidget.max_row+1;
+    GridDef *cols = calloc(ncols, sizeof(GridDef));
+    GridDef *rows = calloc(nrows, sizeof(GridDef));
+    int num_cols_expanding = 0;
+    int num_rows_expanding = 0;
+    int req_width = 0;
+    int req_height = 0;
+    
+    // calculate the minimum size requirements for all columns and rows
+    // we need to run this 2 times: for widgets without colspan/rowspan first
+    // and then again for colspan/rowspan > 1
+    int span_max = 1;
+    for(int r=0;r<2;r++) {
+        for(int i=0;i<w->composite.num_children;i++) {
+            Widget child = w->composite.children[i];
+            GridConstraintRec *constraints = child->core.constraints;
+            
+            if(constraints->grid.colspan > span_max || constraints->grid.rowspan > span_max) {
+                continue;
+            }
+            
+            int x = constraints->grid.x;
+            int y = constraints->grid.y;
+            // make sure ncols/nrows is correct
+            // errors shouldn't happen, unless someone messes up the grid internals
+            if(x >= ncols) {
+                fprintf(stderr, "Error: widget x out of bounds\n");
+                continue;
+            }
+            if(y >= nrows) {
+                fprintf(stderr, "Error: widget y out of bounds\n");
+                continue;
+            }
+            GridDef *col = &cols[x];
+            GridDef *row = &rows[y];
+            
+            if(constraints->grid.hexpand) {
+                if(constraints->grid.colspan > 1) {
+                    // check if any column in the span is expanding
+                    // if not, make the last column expanding
+                    GridDef *last_col = col;
+                    for(int c=x;c<ncols;c++) {
+                        last_col = &cols[c];
+                        if(last_col->expand) {
+                            break;
+                        }
+                    }
+                    last_col->expand = TRUE;
+                } else {
+                    col->expand = TRUE;
+                }
+            }
+            if(constraints->grid.vexpand) {
+                if(constraints->grid.rowspan > 1) {
+                    GridDef *last_row = row;
+                    for(int c=x;c<nrows;c++) {
+                        last_row = &rows[c];
+                        if(last_row->expand) {
+                            break;
+                        }
+                    }
+                    last_row->expand = TRUE;
+                } else {
+                    row->expand = TRUE;
+                }
+            } 
+            
+            // column size
+            if(constraints->grid.colspan > 1) {
+                // check size of all columns in span
+                Dimension span_width = col->size;
+                GridDef *last_col = col;
+                for(int s=x+1;s<ncols;s++) {
+                    last_col = &cols[s];
+                    span_width = last_col->size;
+                    
+                }
+                int diff = constraints->grid.pref_width - span_width;
+                if(diff > 0) {
+                    last_col->size += diff; 
+                }
+            } else if(constraints->grid.pref_width > col->size) {
+                col->size = constraints->grid.pref_width;
+            }
+            // row size
+            if(constraints->grid.rowspan > 1) {
+                Dimension span_height = row->size;
+                GridDef *last_row = row;
+                for(int s=x+1;s<nrows;s++) {
+                    last_row = &rows[s];
+                    span_height = last_row->size;
+                    
+                }
+                int diff = constraints->grid.pref_height - span_height;
+                if(diff > 0) {
+                    last_row->size += diff; 
+                }
+            } else if(constraints->grid.pref_height > row->size) {
+                row->size = constraints->grid.pref_height;
+            }
+        }
+        span_max = 50000; // not sure if this is unreasonable low or high
+    }
+    
+    
+    for(int i=0;i<ncols;i++) {
+        if(cols[i].expand) {
+            num_cols_expanding++;
+        }
+        req_width += cols[i].size;
+    }
+    for(int i=0;i<nrows;i++) {
+        if(rows[i].expand) {
+            num_rows_expanding++;
+        }
+        req_height += rows[i].size;
+    }
+    
+    if(req_width > 0 && req_height > 0) {
+        Widget parent = w->core.parent;
+        Dimension rwidth = req_width;
+        Dimension rheight = req_height;
+        if(rwidth < w->core.width) {
+            //rwidth = w->core.width;
+        }
+        if(rheight < w->core.height) {
+            //rheight = w->core.height;
+        }
+        
+        if(!w->mywidget.sizerequest) {
+            Dimension actual_width, actual_height;
+            w->mywidget.sizerequest = TRUE;
+            XtMakeResizeRequest((Widget)w, req_width, req_height, &actual_width, &actual_height);
+            w->mywidget.sizerequest = FALSE;
+            //printf("size request: %d %d\n", (int)actual_width, (int)actual_height);
+        }
+        
+       
+        
+    }
+    
+    int hexpand = 0;
+    int width_diff = (int)w->core.width - req_width;
+    int hexpand2 = 0;
+    if(width_diff > 0 && num_cols_expanding > 0) {
+        hexpand = width_diff / num_cols_expanding;
+        hexpand2 = width_diff-hexpand*num_cols_expanding;
+    }
+    int x = 0;
+    for(int i=0;i<ncols;i++) {
+        cols[i].pos = x;
+        if(cols[i].expand) {
+            cols[i].size += hexpand + hexpand2;
+        }
+        x += cols[i].size;
+        
+        hexpand2 = 0;
+    }
+    
+    int vexpand = 0;
+    int height_diff = (int)w->core.height - req_height;
+    int vexpand2 = 0;
+    if(height_diff > 0 && num_rows_expanding > 0) {
+        vexpand = height_diff / num_rows_expanding;
+        vexpand2 = height_diff-vexpand*num_rows_expanding;
+    }
+    int y = 0;
+    for(int i=0;i<nrows;i++) {
+        rows[i].pos = y;
+        if(rows[i].expand) {
+            rows[i].size += vexpand + vexpand2;
+        }
+        y += rows[i].size;
+        
+        vexpand2 = 0;
+    }
+    
+    for(int i=0;i<w->composite.num_children;i++) {
+        Widget child = w->composite.children[i];
+        GridConstraintRec *constraints = child->core.constraints;
+        GridDef c = cols[constraints->grid.x];
+        GridDef r = rows[constraints->grid.y];
+        int x = c.pos;
+        int y = r.pos;
+        int width = constraints->grid.pref_width;
+        int height = constraints->grid.pref_height;
+        if(constraints->grid.hfill) {
+            if(constraints->grid.colspan > 1) {
+                Dimension cwidth = 0;
+                for(int j=0;j<constraints->grid.colspan;j++) {
+                    if(constraints->grid.x+j < ncols) {
+                        cwidth += cols[constraints->grid.x+j].size;
+                    }
+                }
+                width = cwidth;
+            } else {
+                width = c.size;
+            }
+        }
+        if(constraints->grid.vfill) {
+            if(constraints->grid.rowspan > 1) {
+                Dimension cheight = 0;
+                for(int j=0;j<constraints->grid.rowspan;j++) {
+                    if(constraints->grid.y+j < nrows) {
+                        cheight += rows[constraints->grid.y+j].size;
+                    }
+                }
+                height = cheight;
+            } else {
+                height = r.size;
+            }
+        }
+        
+        XtConfigureWidget(child, x, y, width, height, child->core.border_width);
+        //printf("child %d %d - %d %d\n", (int)child->core.x, (int)child->core.y, (int)child->core.width, (int)child->core.height);
+    }
+    
+    free(cols);
+    free(rows);
+}
+

mercurial