Sun, 15 Dec 2024 22:16:12 +0100
add menu separator (Motif)
/* * 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); }