add first code of the new Motif implementation, implement buttons newapi

Wed, 04 Dec 2024 18:31:22 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 04 Dec 2024 18:31:22 +0100
branch
newapi
changeset 406
0ebf9d7b23e8
parent 405
a7f18dda6baf
child 407
8ea123dd89db

add first code of the new Motif implementation, implement buttons

application/main.c file | annotate | diff | comparison | revisions
ui/common/context.c file | annotate | diff | comparison | revisions
ui/common/object.c file | annotate | diff | comparison | revisions
ui/motif/Grid.c file | annotate | diff | comparison | revisions
ui/motif/Grid.h file | annotate | diff | comparison | revisions
ui/motif/button.c file | annotate | diff | comparison | revisions
ui/motif/button.h file | annotate | diff | comparison | revisions
ui/motif/container.c file | annotate | diff | comparison | revisions
ui/motif/container.h file | annotate | diff | comparison | revisions
ui/motif/dnd.c file | annotate | diff | comparison | revisions
ui/motif/graphics.c file | annotate | diff | comparison | revisions
ui/motif/graphics.h file | annotate | diff | comparison | revisions
ui/motif/image.c file | annotate | diff | comparison | revisions
ui/motif/label.c file | annotate | diff | comparison | revisions
ui/motif/list.c file | annotate | diff | comparison | revisions
ui/motif/list.h file | annotate | diff | comparison | revisions
ui/motif/menu.c file | annotate | diff | comparison | revisions
ui/motif/menu.h file | annotate | diff | comparison | revisions
ui/motif/objs.mk file | annotate | diff | comparison | revisions
ui/motif/range.c file | annotate | diff | comparison | revisions
ui/motif/range.h file | annotate | diff | comparison | revisions
ui/motif/stock.c file | annotate | diff | comparison | revisions
ui/motif/stock.h file | annotate | diff | comparison | revisions
ui/motif/text.c file | annotate | diff | comparison | revisions
ui/motif/text.h file | annotate | diff | comparison | revisions
ui/motif/toolbar.c file | annotate | diff | comparison | revisions
ui/motif/toolbar.h file | annotate | diff | comparison | revisions
ui/motif/toolkit.c file | annotate | diff | comparison | revisions
ui/motif/toolkit.h file | annotate | diff | comparison | revisions
ui/motif/tree.c file | annotate | diff | comparison | revisions
ui/motif/tree.h file | annotate | diff | comparison | revisions
ui/motif/window.c file | annotate | diff | comparison | revisions
ui/motif/window.h file | annotate | diff | comparison | revisions
ui/ui/toolkit.h file | annotate | diff | comparison | revisions
--- a/application/main.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/application/main.c	Wed Dec 04 18:31:22 2024 +0100
@@ -34,7 +34,7 @@
 #include <cx/buffer.h>
 #include <cx/utils.h>
 
-#ifndef UI_COCOA
+#if !defined(UI_COCOA) && !defined(UI_MOTIF)
 
 typedef struct {
     UiString *str1;
@@ -455,10 +455,22 @@
 
 #endif
 
-#ifdef UI_COCOA
+#if defined(UI_COCOA) || defined(UI_MOTIF)
+
+void action_button(UiEvent *event, void *data) {
+    printf("action_button\n");
+}
 
 void application_startup(UiEvent *event, void *data) {
-
+    UiObject *obj = ui_window("Test", NULL);
+    ui_button(obj, .label = "Test Button", .onclick = action_button);
+    ui_togglebutton(obj, .label = "Togglebutton");
+    ui_checkbox(obj, .label = "Checkbox", .enable_group = 123);
+    ui_checkbox(obj, .label = "Checkbox Disabled", .groups = UI_GROUPS(123));
+    ui_radiobutton(obj, .label = "Radio 1", .varname = "radio");
+    ui_radiobutton(obj, .label = "Radio 2", .varname = "radio");
+    
+    ui_show(obj);
 }
 
 int main(int argc, char** argv) {
--- a/ui/common/context.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/common/context.c	Wed Dec 04 18:31:22 2024 +0100
@@ -187,6 +187,13 @@
 }
 
 UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) {
+    if(ctx->vars_unbound) {
+        UiVar *unbound = cxMapGet(ctx->vars_unbound, name);
+        if(unbound) {
+            return unbound;
+        }
+    }
+    
     UiVar *var = uic_get_var(ctx, name);
     if(var) {
         if(var->type == type) {
--- a/ui/common/object.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/common/object.c	Wed Dec 04 18:31:22 2024 +0100
@@ -64,16 +64,17 @@
     obj->ref++;
 }
 
-void ui_object_unref(UiObject *obj) {
+int ui_object_unref(UiObject *obj) {
     // it is possible to have 0 references, in case
     // a window was created but ui_show was never called
     if(obj->ref == 0 || --obj->ref == 0) {
         if(obj->destroy) {
             obj->destroy(obj);
-        } else {
-            uic_object_destroy(obj);
         }
+        uic_object_destroy(obj);
+        return 0;
     }
+    return 1;
 }
 
 void uic_object_destroy(UiObject *obj) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/motif/Grid.c	Wed Dec 04 18:31:22 2024 +0100
@@ -0,0 +1,487 @@
+/*
+ * 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;
+    
+    for(int i=0;i<w->composite.num_children;i++) {
+        Widget child = w->composite.children[i];
+        GridConstraintRec *constraints = child->core.constraints;
+        if(constraints->grid.x < ncols) {
+            if(constraints->grid.hexpand) {
+                cols[constraints->grid.x].expand = TRUE;
+            }
+            if(constraints->grid.pref_width > cols[constraints->grid.x].size) {
+                cols[constraints->grid.x].size = constraints->grid.pref_width;
+            }
+        } else {
+            fprintf(stderr, "Error: x >= ncols\n");
+        }
+        if(constraints->grid.y < nrows) {
+            if(constraints->grid.vexpand) {
+                rows[constraints->grid.y].expand = TRUE;
+            }
+            if(constraints->grid.pref_height > rows[constraints->grid.y].size) {
+                rows[constraints->grid.y].size = constraints->grid.pref_height;
+            }
+        } else {
+            fprintf(stderr, "Error: y >= nrows\n");
+        }
+    }
+    
+    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;
+    }
+    
+    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);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/motif/Grid.h	Wed Dec 04 18:31:22 2024 +0100
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+#ifndef GRID_H
+#define GRID_H
+
+#include <X11/Intrinsic.h>
+#include <X11/IntrinsicP.h>
+#include <Xm/XmAll.h>
+#include <Xm/Primitive.h>
+#include <Xm/PrimitiveP.h>
+#include <Xm/ManagerP.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define gridColumn "gridColumn"
+#define gridRow "gridRow"
+#define gridColspan "gridColspan"
+#define gridRowspan "gridRowspan"
+#define gridHExpand "gridHExpand"
+#define gridVExpand "gridVExpand"
+#define gridHFill "gridHFill"
+#define gridVFill "gridVFill"
+#define gridMarginLeft "gridMarginLeft"
+#define gridMarginRight "gridMarginRight"
+#define gridMarginTop "gridMarginTop"
+#define gridMarginBottom "gridMarginBottom"
+    
+
+typedef struct GridDef {
+    Dimension size;
+    Dimension pos;
+    Boolean expand;
+} GridDef;
+    
+typedef struct GridClassPart {
+    int test;
+} GridClassPart;
+    
+typedef struct GridClassRec {
+    CoreClassPart        core_class;
+    CompositeClassPart   composite_class;
+    ConstraintClassPart  constraint_class;
+    XmManagerClassPart  manager_class;
+    GridClassPart    mywidgetclass;
+} GridClassRec;
+
+
+typedef struct GridPart {
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
+    int max_col;
+    int max_row;
+} GridPart;
+
+typedef struct GridRec {
+    CorePart	    core;
+    CompositePart   composite;
+    ConstraintPart  constraint;
+    XmManagerPart   manager;
+    GridPart    mywidget;
+} GridRec;
+
+typedef struct GridContraintPart {
+    Dimension x;
+    Dimension y;
+    Dimension margin_left;
+    Dimension margin_right;
+    Dimension margin_top;
+    Dimension margin_bottom;
+    Boolean hexpand;
+    Boolean vexpand;
+    Boolean hfill;
+    Boolean vfill;
+    Dimension colspan;
+    Dimension rowspan;
+    Dimension pref_width;
+    Dimension pref_height;
+} GridContraintPart;
+
+typedef struct GridConstraintRec {
+    XmManagerConstraintPart manager;
+    GridContraintPart grid;
+} GridConstraintRec;
+
+typedef GridRec* MyWidget;
+
+extern WidgetClass gridClass;
+
+void grid_class_initialize();
+void grid_initialize();
+void grid_realize();
+void grid_destroy();
+void grid_resize();
+void grid_expose();
+Boolean grid_set_values();
+Boolean grid_acceptfocus(Widget , Time*);
+
+void grid_place_children(MyWidget w);
+
+void grid_getfocus();
+void grid_loosefocus();
+
+void grid_constraint_init(
+    Widget	request,
+    Widget	neww,
+    ArgList	args,
+    Cardinal*	num_args
+);
+
+XtGeometryResult GridGeometryManager(Widget	widget, XtWidgetGeometry *request, XtWidgetGeometry *reply);
+void GridChangeManaged(Widget widget);
+Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRID_H */
+
--- a/ui/motif/button.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/button.c	Wed Dec 04 18:31:22 2024 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2014 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -38,69 +38,54 @@
 #include <cx/array_list.h>
 #include <cx/compare.h>
 
+#include <Xm/XmAll.h>
 
-UIWIDGET ui_button(UiObject *obj, char *label, ui_callback f, void *data) {
-    UiContainer *ct = uic_get_current_container(obj);
-    XmString str = XmStringCreateLocalized(label);
+
+UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) {
+    Arg xargs[16];
+    int n = 0;
     
-    int n = 0;
-    Arg args[16];
+    UiContainerPrivate *ctn = ui_obj_container(obj);
+    UI_APPLY_LAYOUT(ctn->layout, args);
     
-    XtSetArg(args[n], XmNlabelString, str);
-    n++;
+    Widget parent = ctn->prepare(ctn, xargs, &n);
     
-    Widget parent = ct->prepare(ct, args, &n, FALSE);
-    Widget button = XmCreatePushButton(parent, "button", args, n);
-    ct->add(ct, button);
+    XmString label = NULL;
+    if(args.label) {
+        label = XmStringCreateLocalized((char*)args.label);
+        XtSetArg(xargs[n], XmNlabelString, label); n++;
+    }
     
-    if(f) {
-        UiEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = data;
-        event->callback = f;
-        event->value = 0;
+    char *name = args.name ? (char*)args.name : "button";
+    Widget button = XmCreatePushButton(parent, name, xargs, n);
+    XtManageChild(button);
+    ctn->add(ctn, button);
+    
+    ui_set_widget_groups(obj->ctx, button, args.groups);
+    
+    if(args.onclick) {
+        UiEventData *eventdata = malloc(sizeof(UiEventData));
+        eventdata->callback = args.onclick;
+        eventdata->userdata = args.onclickdata;
+        eventdata->obj = obj;
+        eventdata->value = 0;
         XtAddCallback(
                 button,
                 XmNactivateCallback,
                 (XtCallbackProc)ui_push_button_callback,
-                event);
+                eventdata);
+       XtAddCallback(
+                button,
+                XmNdestroyCallback,
+                (XtCallbackProc)ui_destroy_eventdata,
+                eventdata);
     }
     
-    XtManageChild(button);
     
+    XmStringFree(label);
     return button;
 }
 
-// wrapper
-int64_t ui_toggle_button_get(UiInteger *i) {
-    int state = 0;
-    XtVaGetValues(i->obj, XmNset, &state, NULL);
-    i->value = state;
-    return state;
-}
-
-void ui_toggle_button_set(UiInteger *i, int64_t value) {
-    Arg arg;
-    XtSetArg(arg, XmNset, value);
-    XtSetValues(i->obj, &arg, 1);
-    i->value = value;
-}
-
-void ui_toggle_button_callback(
-        Widget widget,
-        UiEventData *event,
-        XmToggleButtonCallbackStruct *tb)
-{
-    UiEvent e;
-    e.obj = event->obj;
-    e.window = event->obj->window;
-    // TODO: e.document
-    e.intval = tb->set;
-    event->callback(&e, event->userdata); 
-}
-
 void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d) {
     UiEvent e;
     e.obj = event->obj;
@@ -110,105 +95,295 @@
     event->callback(&e, event->userdata);
 }
 
+UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) {
+    Arg xargs[16];
+    int n = 0;
+    
+    UiContainerPrivate *ctn = ui_obj_container(obj);
+    UI_APPLY_LAYOUT(ctn->layout, args);
+    
+    Widget parent = ctn->prepare(ctn, xargs, &n);
+    XtSetArg(xargs[n], XmNfillOnSelect, True); n++;
+    XtSetArg(xargs[n], XmNindicatorOn, False); n++;
+    
+    XmString label = NULL;
+    if(args.label) {
+        label = XmStringCreateLocalized((char*)args.label);
+        XtSetArg(xargs[n], XmNlabelString, label); n++;
+    }
+    
+    char *name = args.name ? (char*)args.name : "togglebutton";
+    Widget button = XmCreateToggleButton(parent, name, xargs, n);
+    XtManageChild(button);
+    ctn->add(ctn, button);
+    
+    ui_set_widget_groups(obj->ctx, button, args.groups);
+    
+    ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group);
+    
+    XmStringFree(label);
+    return button;
+}
 
-static void radio_callback(
-        Widget widget,
-        RadioEventData *event,
-        XmToggleButtonCallbackStruct *tb)
-{
-    if(tb->set) {
-        RadioButtonGroup *group = event->group;
-        if(group->current) {
-            Arg arg;
-            XtSetArg(arg, XmNset, FALSE);
-            XtSetValues(group->current, &arg, 1);
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) {
+    Arg xargs[16];
+    int n = 0;
+    
+    UiContainerPrivate *ctn = ui_obj_container(obj);
+    UI_APPLY_LAYOUT(ctn->layout, args);
+    
+    Widget parent = ctn->prepare(ctn, xargs, &n);
+    
+    XmString label = NULL;
+    if(args.label) {
+        label = XmStringCreateLocalized((char*)args.label);
+        XtSetArg(xargs[n], XmNlabelString, label); n++;
+    }
+    
+    char *name = args.name ? (char*)args.name : "button";
+    Widget button = XmCreateToggleButton(parent, name, xargs, n);
+    XtManageChild(button);
+    ctn->add(ctn, button);
+    
+    ui_set_widget_groups(obj->ctx, button, args.groups);
+    
+    ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group);
+    
+    XmStringFree(label);
+    return button;
+}
+
+UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) {
+    return ui_checkbox_create(obj, args);
+}
+
+static void togglebutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) {
+    if(event->value > 0) {
+        // button in configured to enable/disable states
+        if(tb->set) {
+            ui_set_group(event->obj->ctx, event->value);
+        } else {
+            ui_unset_group(event->obj->ctx, event->value);
         }
-        group->current = widget;
+    }
+    
+    UiEvent e;
+    e.obj = event->obj;
+    e.window = e.obj->window;
+    e.document = e.obj->ctx->document;
+    e.eventdata = NULL;
+    e.intval = XmToggleButtonGetState(w);
+    
+    if(event->callback) {
+        event->callback(&e, event->userdata);
+    }
+    
+    if(event->var && event->var->value) {
+        UiInteger *v = event->var->value;
+        v->value = e.intval;
+        ui_notify_evt(v->observers, &e);
     }
 }
 
-UIWIDGET ui_radiobutton(UiObject *obj, char *label, UiInteger *rgroup) {
-    UiContainer *ct = uic_get_current_container(obj);
-    XmString str = XmStringCreateLocalized(label);
-    
-    int n = 0;
-    Arg args[16];
-    
-    XtSetArg(args[n], XmNlabelString, str);
-    n++;
-    XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY_ROUND);
-    n++;
-    
-    Widget parent = ct->prepare(ct, args, &n, FALSE);
-    Widget button = XmCreateToggleButton(parent, "radiobutton", args, n);
-    ct->add(ct, button);
-    
-    if(rgroup) {
-        RadioButtonGroup *group;
-        if(rgroup->obj) {
-            group = rgroup->obj;
-            if(!group->buttons) {
-                group->buttons = cxArrayListCreate(cxDefaultAllocator, cx_cmp_uintptr, CX_STORE_POINTERS, 8);
-            }
-            cxListAdd(group->buttons, button);
-            group->ref++;
-        } else {
-            group = malloc(sizeof(RadioButtonGroup));
-            group->buttons = cxArrayListCreate(cxDefaultAllocator, cx_cmp_uintptr, CX_STORE_POINTERS, 8);
-            cxListAdd(group->buttons, button);
-            group->current = button;
-            // this is the first button in the radiobutton group
-            // so we should enable it
-            Arg arg;
-            XtSetArg(arg, XmNset, TRUE);
-            XtSetValues(button, &arg, 1);
-            rgroup->obj = group;
-            
-            group->current = button;
+void ui_bind_togglebutton(
+        UiObject *obj,
+        Widget widget,
+        const char *varname,
+        UiInteger *value,
+        ui_callback onchange,
+        void *onchangedata,
+        int enable_state)
+{
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER);
+    if(var) {
+        value = (UiInteger*)var->value;
+        value->obj = widget;
+        value->get = ui_togglebutton_get;
+        value->set = ui_togglebutton_set;
+        
+        if(value->value) {
+            XmToggleButtonSetState(widget, True, False);
         }
-        
-        RadioEventData *event = malloc(sizeof(RadioEventData));
-        event->obj = obj;
-        event->callback = NULL;
-        event->userdata = NULL;
-        event->group = group;
-        XtAddCallback(
-            button,
-            XmNvalueChangedCallback,
-            (XtCallbackProc)radio_callback,
-            event);
-        
-        rgroup->get = ui_radiobutton_get;
-        rgroup->set = ui_radiobutton_set;
     }
     
-    XtManageChild(button); 
-    return button;
+    UiVarEventData *event = malloc(sizeof(UiVarEventData));
+    event->obj = obj;
+    event->callback = onchange;
+    event->userdata = onchangedata;
+    event->var = var;
+    event->observers = NULL;
+    event->value = enable_state;
+    XtAddCallback(
+            widget,
+            XmNvalueChangedCallback,
+            (XtCallbackProc)togglebutton_changed,
+            event);
+    XtAddCallback(
+            widget,
+            XmNdestroyCallback,
+            (XtCallbackProc)ui_destroy_eventdata,
+            event);
+}
+
+int64_t ui_togglebutton_get(UiInteger *i) {
+    Widget togglebutton = i->obj;
+    Boolean state = XmToggleButtonGetState(togglebutton);
+    i->value = state;
+    return state;
+}
+
+void ui_togglebutton_set(UiInteger *i, int64_t value) {
+    Widget togglebutton = i->obj;
+    i->value = value;
+    XmToggleButtonSetState(togglebutton, (Boolean)value, False);
+}
+
+static void destroy_list(Widget w, CxList *list, XtPointer d) {
+    cxListDestroy(list);
 }
 
-int64_t ui_radiobutton_get(UiInteger *value) {
-    RadioButtonGroup *group = value->obj;
+static void radiobutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) {
+    if(event->value > 0) {
+        // button in configured to enable/disable states
+        if(tb->set) {
+            ui_set_group(event->obj->ctx, event->value);
+        } else {
+            ui_unset_group(event->obj->ctx, event->value);
+        }
+    }
+    
+    if(!tb->set) {
+        return; // only handle set-events
+    }
     
-    int i = cxListFind(group->buttons, group->current);
-    if (i >= 0) {
-        value->value = i;
-        return i;
-    } else {
-        return 0;
+    UiInteger *value = NULL;
+    int64_t v = 0;
+    if(event->var) {
+        value = event->var->value;
+        // find widget index and update all radiobuttons
+        // the UiInteger value must always be up-to-date
+        CxList *list = value->obj;
+        CxIterator i = cxListIterator(list);
+        cx_foreach(Widget, button, i) {
+            Boolean state = False;
+            if(button == w) {
+                value->value = i.index+1; // update value
+                state = True;
+            }
+            XmToggleButtonSetState(button, state, False);
+        }
+        v = value->value;
+    }
+    
+    UiEvent e;
+    e.obj = event->obj;
+    e.window = e.obj->window;
+    e.document = e.obj->ctx->document;
+    e.eventdata = value;
+    e.intval = v;
+    
+    if(event->callback) {
+        event->callback(&e, event->userdata);
+    }
+    
+    if(value) {
+        ui_notify_evt(value->observers, &e);
     }
 }
 
-void ui_radiobutton_set(UiInteger *value, int64_t i) {
-    RadioButtonGroup *group = value->obj;
-    Arg arg;
+UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args) {
+    Arg xargs[16];
+    int n = 0;
+    
+    UiContainerPrivate *ctn = ui_obj_container(obj);
+    UI_APPLY_LAYOUT(ctn->layout, args);
+    
+    Widget parent = ctn->prepare(ctn, xargs, &n);
+    XtSetArg(xargs[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++;
+    XmString label = NULL;
+    if(args.label) {
+        label = XmStringCreateLocalized((char*)args.label);
+        XtSetArg(xargs[n], XmNlabelString, label); n++;
+    }
+    
+    char *name = args.name ? (char*)args.name : "button";
+    Widget button = XmCreateToggleButton(parent, name, xargs, n);
+    XtManageChild(button);
+    ctn->add(ctn, button);
+    
+    ui_set_widget_groups(obj->ctx, button, args.groups);
     
-    XtSetArg(arg, XmNset, FALSE);
-    XtSetValues(group->current, &arg, 1);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_INTEGER);
+    if(var) {
+        UiInteger *value = var->value;
+        CxList *rb = value->obj;
+        if(!rb) {
+            // first button in the radiobutton group
+            // create a list for all buttons and use the list as value obj
+            rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+            value->obj = rb;
+            value->get = ui_radiobutton_get;
+            value->set = ui_radiobutton_set;
+            
+            // the first radio button is also responsible for cleanup
+            XtAddCallback(
+                    button,
+                    XmNdestroyCallback,
+                    (XtCallbackProc)destroy_list,
+                    rb);
+        }
+        cxListAdd(rb, button);
+        
+        // set the radiobutton state, if the value is already set
+        if(cxListSize(rb) == value->value) {
+            XmToggleButtonSetState(button, True, False);
+        }
+    }
     
-    Widget button = cxListAt(group->buttons, i);
-    if(button) {
-        XtSetArg(arg, XmNset, TRUE);
-        XtSetValues(button, &arg, 1);
-        group->current = button;
+    // the radio button needs to handle change events to update all
+    // other buttons in the radio button group
+    UiVarEventData *event = malloc(sizeof(UiVarEventData));
+    event->obj = obj;
+    event->callback = args.onchange;
+    event->userdata = args.onchangedata;
+    event->observers = NULL;
+    event->var = var;
+    event->value = args.enable_group;
+    XtAddCallback(
+            button,
+            XmNvalueChangedCallback,
+            (XtCallbackProc)radiobutton_changed,
+            event);
+    XtAddCallback(
+            button,
+            XmNdestroyCallback,
+            (XtCallbackProc)ui_destroy_eventdata,
+            event);
+    
+    XmStringFree(label);
+    return button;
+    
+    
+}
+
+int64_t ui_radiobutton_get(UiInteger *i) {
+    // the UiInteger should be updated automatically by change events
+    return i->value;
+}
+
+void ui_radiobutton_set(UiInteger *i, int64_t value) {
+    CxList *list = i->obj;
+    if(i->value > 0) {
+        Widget current = cxListAt(list, i->value-1);
+        if(current) {
+            XmToggleButtonSetState(current, False, False);
+        }
+    }
+    if(value > 0 && value <= cxListSize(list)) {
+        Widget button = cxListAt(list, value-1);
+        if(button) {
+            XmToggleButtonSetState(button, True, False);
+            i->value = value;
+        }
     }
 }
--- a/ui/motif/button.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/button.h	Wed Dec 04 18:31:22 2024 +0100
@@ -36,30 +36,22 @@
 extern "C" {
 #endif
 
-typedef struct {
-    CxList  *buttons;
-    Widget  current;
-    int     ref;
-} RadioButtonGroup;
-
-typedef struct {
-    UiObject         *obj;
-    ui_callback      callback;
-    void             *userdata;
-    RadioButtonGroup *group;
-} RadioEventData;
-
-// wrapper
-int64_t ui_toggle_button_get(UiInteger *i);
-void ui_toggle_button_set(UiInteger *i, int64_t value);
-void ui_toggle_button_callback(
-        Widget widget,
-        UiEventData *data,
-        XmToggleButtonCallbackStruct *e);
 void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d);
 
-int64_t ui_radiobutton_get(UiInteger *value);
-void ui_radiobutton_set(UiInteger *value, int64_t i);
+void ui_bind_togglebutton(
+        UiObject *obj,
+        Widget widget,
+        const char *varname,
+        UiInteger *value,
+        ui_callback onchange,
+        void *onchangedata,
+        int enable_state);
+
+int64_t ui_togglebutton_get(UiInteger *i);
+void ui_togglebutton_set(UiInteger *i, int64_t value);
+
+int64_t ui_radiobutton_get(UiInteger *i);
+void ui_radiobutton_set(UiInteger *i, int64_t value);
 
 #ifdef	__cplusplus
 }
--- a/ui/motif/container.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/container.c	Wed Dec 04 18:31:22 2024 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2014 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -34,781 +34,51 @@
 #include "../common/context.h"
 #include "../common/object.h"
 
-#include <cx/array_list.h>
-#include <cx/linked_list.h>
-#include <cx/compare.h>
-
-#define UI_GRID_MAX_COLUMNS 512
-
-static UiBool ui_lb2bool(UiLayoutBool b) {
-    return b == UI_LAYOUT_TRUE ? TRUE : FALSE;
-}
-
-static UiLayoutBool ui_bool2lb(UiBool b) {
-    return b ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE;
-}
-
-
-UiContainer* ui_frame_container(UiObject *obj, Widget frame) {
-    UiContainer *ct = cxCalloc(
-            obj->ctx->allocator,
-            1,
-            sizeof(UiContainer));
-    ct->widget = frame;
-    ct->prepare = ui_frame_container_prepare;
-    ct->add = ui_frame_container_add;
-    return ct;
-}
-
-Widget ui_frame_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) {
-    return ct->widget;
-}
-
-void ui_frame_container_add(UiContainer *ct, Widget widget) {
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
-
-UiContainer* ui_box_container(UiObject *obj, Widget box, int margin, int spacing, UiBoxOrientation orientation) {
-    UiBoxContainer *ct = cxCalloc(
-            obj->ctx->allocator,
-            1,
-            sizeof(UiBoxContainer));
-    ct->container.widget = box;
-    ct->container.prepare = ui_box_container_prepare;
-    ct->container.add = ui_box_container_add;
-    ct->orientation = orientation;
-    ct->margin = margin;
-    ct->spacing = spacing;
-    return (UiContainer*)ct;
-}
+#include "Grid.h"
 
-Widget ui_box_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) {
-    UiBoxContainer *bc = (UiBoxContainer*)ct;
-    if(ct->layout.fill != UI_LAYOUT_UNDEFINED) {
-        fill = ui_lb2bool(ct->layout.fill);
-    }
-    
-    if(bc->has_fill && fill) {
-        fprintf(stderr, "UiError: container has 2 filled widgets");
-        fill = FALSE;
-    }
-    if(fill) {
-        bc->has_fill = TRUE;
-    }
-    
-    int a = *n;
-    // determine fixed and dynamic attachments
-    void *f1;
-    void *f2;
-    void *d1;
-    void *d2;
-    void *w1;
-    void *w2;
-    if(bc->orientation == UI_BOX_VERTICAL) {
-        f1 = XmNleftAttachment;
-        f2 = XmNrightAttachment;
-        d1 = XmNtopAttachment;
-        d2 = XmNbottomAttachment;
-        w1 = XmNtopWidget;
-        w2 = XmNbottomWidget;
-        
-        // margin/spacing
-        XtSetArg(args[a], XmNleftOffset, bc->margin); a++;
-        XtSetArg(args[a], XmNrightOffset, bc->margin); a++;
-        
-        XtSetArg(args[a], XmNtopOffset, bc->prev_widget ? bc->spacing : bc->margin); a++;
-    } else {
-        f1 = XmNtopAttachment;
-        f2 = XmNbottomAttachment;
-        d1 = XmNleftAttachment;
-        d2 = XmNrightAttachment;
-        w1 = XmNleftWidget;
-        w2 = XmNrightWidget;
-        
-        // margin/spacing
-        XtSetArg(args[a], XmNtopOffset, bc->margin); a++;
-        XtSetArg(args[a], XmNbottomOffset, bc->margin); a++;
-        
-        XtSetArg(args[a], XmNleftOffset, bc->prev_widget ? bc->spacing : bc->margin); a++;
-    }
-    XtSetArg(args[a], f1, XmATTACH_FORM); a++;
-    XtSetArg(args[a], f2, XmATTACH_FORM); a++;
-
-    if(fill) {
-        XtSetArg(args[a], d2, XmATTACH_FORM); a++;
-    }
-    if(bc->prev_widget) {
-        XtSetArg(args[a], d1, XmATTACH_WIDGET); a++;
-        XtSetArg(args[a], w1, bc->prev_widget); a++;
-    } else {
-        XtSetArg(args[a], d1, XmATTACH_FORM); a++;
-    }
-    
-    *n = a;
-    return ct->widget;
-}
-
-void ui_box_container_add(UiContainer *ct, Widget widget) {
-    UiBoxContainer *bc = (UiBoxContainer*)ct;
-    // determine dynamic attachments
-    void *d1;
-    void *d2;
-    void *w1;
-    void *w2;
-    if(bc->orientation == UI_BOX_VERTICAL) {
-        d1 = XmNtopAttachment;
-        d2 = XmNbottomAttachment;
-        w1 = XmNtopWidget;
-        w2 = XmNbottomWidget;
-        
-    } else {
-        d1 = XmNleftAttachment;
-        d2 = XmNrightAttachment;
-        w1 = XmNleftWidget;
-        w2 = XmNrightWidget;
-    }
-    
-    if(bc->prev_widget) {
-        int v = 0;
-        XtVaGetValues(bc->prev_widget, d2, &v, NULL);
-        if(v == XmATTACH_FORM) {
-            XtVaSetValues(
-                    bc->prev_widget,
-                    d2,
-                    XmATTACH_WIDGET,
-                    w2,
-                    widget,
-                    NULL);
-            XtVaSetValues(
-                    widget,
-                    d1,
-                    XmATTACH_NONE,
-                    d2,
-                    XmATTACH_FORM,
-                    NULL);
-        }
-    }
-    bc->prev_widget = widget;
-    
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
-UiContainer* ui_grid_container(UiObject *obj, Widget form, int columnspacing, int rowspacing) {
-    UiGridContainer *ct = cxCalloc(
-            obj->ctx->allocator,
-            1,
-            sizeof(UiGridContainer));
-    ct->container.widget = form;
-    ct->container.prepare = ui_grid_container_prepare;
-    ct->container.add = ui_grid_container_add;
-    ct->columnspacing = columnspacing;
-    ct->rowspacing = rowspacing;
-    ct->lines = cxLinkedListCreateSimple(CX_STORE_POINTERS);
-    return (UiContainer*)ct;
-}
-
-void ui_grid_newline(UiGridContainer *grid) {
-    if(grid->current) {
-        grid->current = NULL;
-    }
-    grid->container.layout.newline = FALSE;
-}
-
-Widget ui_grid_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) {
-    UiGridContainer *grid = (UiGridContainer*)ct;
-    if(ct->layout.newline) {
-        ui_grid_newline(grid);
-    }
-    return ct->widget;
+UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orientation) {
+    UiBoxContainer *ctn = ui_malloc(obj->ctx, sizeof(UiBoxContainer));
+    memset(ctn, 0, sizeof(UiBoxContainer));
+    ctn->container.prepare = orientation == UI_BOX_VERTICAL ? ui_vbox_prepare : ui_hbox_prepare;
+    ctn->container.add = ui_box_container_add;
+    ctn->container.widget = grid;
+    ctn->n = 0;
+    return (UiContainerX*)ctn;
 }
 
-void ui_grid_container_add(UiContainer *ct, Widget widget) {
-    UiGridContainer *grid = (UiGridContainer*)ct;
-    
-    if(grid->current) {
-        cxListAdd(grid->current, widget);
-    } else {
-        grid->current = cxLinkedListCreateSimple(CX_STORE_POINTERS);
-        cxListAdd(grid->current, widget);
-        cxListAdd(grid->lines, grid->current);
-    }
-    
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
-static void ui_grid_resize(Widget widget, XtPointer udata, XtPointer cdata) {
-    UiGridContainer *grid = udata;
-    
-    CxList *rowdim = cxArrayListCreateSimple(sizeof(int), grid->lines->size);
-    int coldim[UI_GRID_MAX_COLUMNS];
-    memset(coldim, 0, UI_GRID_MAX_COLUMNS*sizeof(int));
-    int numcol = 0;
-    
-    // get the minimum size of the columns and rows
-    int sumw = 0;
-    int sumh = 0;
-    CxIterator lineIterator = cxListIterator(grid->lines);
-    cx_foreach(CxList *, row, lineIterator) {
-        int rheight = 0;
-        int i=0;
-        int sum_width = 0;
-        CxIterator colIterator = cxListIterator(row);
-        cx_foreach(Widget, w, colIterator) {
-            int widget_width = 0;
-            int widget_height = 0;
-            XtVaGetValues(
-                    w,
-                    XmNwidth,
-                    &widget_width,
-                    XmNheight,
-                    &widget_height, 
-                    NULL);
-            
-            // get the maximum height in this row
-            if(widget_height > rheight) {
-                rheight = widget_height;
-            }
-            
-            // get the maximum width in this column
-            if(widget_width > coldim[i]) {
-                coldim[i] = widget_width;
-            }
-            sum_width += widget_width;
-            if(sum_width > sumw) {
-                sumw = sum_width;
-            }
-            
-            i++;
-            if(i > numcol) {
-                numcol = i;
-            }
-        }
-        cxListAdd(rowdim, &rheight);
-        sumh += rheight;
-    }
-    
-    // check container size
-    int gwidth = 0;
-    int gheight = 0;
-    XtVaGetValues(widget, XmNwidth, &gwidth, XmNheight, &gheight, NULL);
-    if(gwidth < sumw || gheight < sumh) {
-        XtVaSetValues(widget, XmNwidth, sumw, XmNheight, sumh, NULL);
-        cxListDestroy(rowdim);
-        return;
-    }
-    
-    
-    // adjust the positions of all children
-    int y = 0;
-    lineIterator = cxListIterator(grid->lines);
-    cx_foreach(CxList *, row, lineIterator) {
-        int x = 0;       
-        int i=0;
-        int *rowheight = cxListAt(rowdim, lineIterator.index);
-        CxIterator colIterator = cxListIterator(row);
-        cx_foreach(Widget, w, colIterator) {
-            XtVaSetValues(
-                    w,
-                    XmNx, x,
-                    XmNy, y,
-                    XmNwidth, coldim[i],
-                    XmNheight, *rowheight,
-                    NULL);
-            
-            x += coldim[i];
-            i++;
-        }
-        y += *rowheight;
-    }
-    
-    cxListDestroy(rowdim);
-}
-
-UiContainer* ui_scrolledwindow_container(UiObject *obj, Widget scrolledwindow) {
-    UiContainer *ct = cxCalloc(
-            obj->ctx->allocator,
-            1,
-            sizeof(UiContainer));
-    ct->widget = scrolledwindow;
-    ct->prepare = ui_scrolledwindow_container_prepare;
-    ct->add = ui_scrolledwindow_container_add;
-    return ct;
-}
-
-Widget ui_scrolledwindow_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) {
-    return ct->widget;
-}
-
-void ui_scrolledwindow_container_add(UiContainer *ct, Widget widget) {
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
-
-UiContainer* ui_tabview_container(UiObject *obj, Widget frame) {
-    UiTabViewContainer *ct = cxCalloc(
-            obj->ctx->allocator,
-            1,
-            sizeof(UiTabViewContainer));
-    ct->context = obj->ctx;
-    ct->container.widget = frame;
-    ct->container.prepare = ui_tabview_container_prepare;
-    ct->container.add = ui_tabview_container_add;
-    ct->tabs = cxArrayListCreate(cxDefaultAllocator, cx_cmp_uintptr, CX_STORE_POINTERS, 16);
-    return (UiContainer*)ct;
-}
-
-Widget ui_tabview_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) {
+static Widget ui_box_container_prepare(UiBoxContainer *box, Arg *args, int *n) {
     int a = *n;
-    XtSetArg(args[a], XmNleftAttachment, XmATTACH_FORM); a++;
-    XtSetArg(args[a], XmNrightAttachment, XmATTACH_FORM); a++;
-    XtSetArg(args[a], XmNtopAttachment, XmATTACH_FORM); a++;
-    XtSetArg(args[a], XmNbottomAttachment, XmATTACH_FORM); a++;
-    *n = a;
-    return ct->widget;
-}
-
-void ui_tabview_container_add(UiContainer *ct, Widget widget) {
-    UiTabViewContainer *tabview = (UiTabViewContainer*)ct; 
-    
-    if(tabview->current) {
-        XtUnmanageChild(tabview->current);
-    }
-
-    tabview->current = widget;
-    cxListAdd(tabview->tabs, widget);
-    
-    ui_select_tab(ct->widget, 0);
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
-UIWIDGET ui_box(UiObject *obj, int margin, int spacing, UiBoxOrientation orientation) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    Arg args[16];
-    int n = 0;
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    Widget form = XmCreateForm(parent, "vbox", args, n);
-    ct->add(ct, form);
-    XtManageChild(form);
-    
-    UiObject *newobj = uic_object_new(obj, form);
-    newobj->container = ui_box_container(obj, form, margin, spacing, orientation);
-    uic_obj_add(obj, newobj);
-    
-    return form;
-}
-
-UIWIDGET ui_vbox(UiObject *obj) {
-    return ui_box(obj, 0, 0, UI_BOX_VERTICAL);
-}
-
-UIWIDGET ui_hbox(UiObject *obj) {
-    return ui_box(obj, 0, 0, UI_BOX_HORIZONTAL);
-}
-
-UIWIDGET ui_vbox_sp(UiObject *obj, int margin, int spacing) {
-    return ui_box(obj, margin, spacing, UI_BOX_VERTICAL);
-}
-
-UIWIDGET ui_hbox_sp(UiObject *obj, int margin, int spacing) {
-    return ui_box(obj, margin, spacing, UI_BOX_HORIZONTAL);
+    box->n++;
+    return box->container.widget;
 }
 
-UIWIDGET ui_grid(UiObject *obj) {
-    return ui_grid_sp(obj, 0, 0, 0);
-}
-
-UIWIDGET ui_grid_sp(UiObject *obj, int margin, int columnspacing, int rowspacing) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    Arg args[16];
-    int n = 0;
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    Widget grid = XmCreateDrawingArea(parent, "grid", args, n);
-    ct->add(ct, grid);
-    XtManageChild(grid);
-    
-    UiObject *newobj = uic_object_new(obj, grid);
-    newobj->container = ui_grid_container(obj, grid, columnspacing, rowspacing);
-    uic_obj_add(obj, newobj);
-    
-    XtAddCallback (grid, XmNresizeCallback , ui_grid_resize, newobj->container);
-    
-    return grid;
-}
-
-UIWIDGET ui_scrolledwindow(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    Arg args[16];
-    int n = 0;
-    XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); // TODO: dosn't work, use XmAPPLICATION_DEFINED
-    n++;
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    Widget scrolledwindow = XmCreateScrolledWindow(parent, "scrolledwindow", args, n);
-    ct->add(ct, scrolledwindow);
-    XtManageChild(scrolledwindow);
-    
-    UiObject *newobj = uic_object_new(obj, scrolledwindow);
-    newobj->container = ui_scrolledwindow_container(obj, scrolledwindow);
-    uic_obj_add(obj, newobj);
-    
-    return scrolledwindow;
-}
-
-UIWIDGET ui_sidebar(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    Arg args[16];
-    int n = 0;
-    
-    XtSetArg(args[n], XmNorientation, XmHORIZONTAL);
-    n++;
-    
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    Widget pane = XmCreatePanedWindow(parent, "pane", args, n);
-    ct->add(ct, pane);
-    XtManageChild(pane);
-    
-    // add sidebar widget
-    Widget sidebar = XmCreateForm(pane, "sidebar", args, 0);
-    XtManageChild(sidebar);
-    
-    UiObject *left = uic_object_new(obj, sidebar);
-    left->container = ui_box_container(left, sidebar, 0, 0, UI_BOX_VERTICAL);
-    
-    // add content widget
-    XtSetArg (args[0], XmNpaneMaximum, 8000);
-    Widget content = XmCreateForm(pane, "content_area", args, 1);
-    XtManageChild(content);
-    
-    UiObject *right = uic_object_new(obj, content);
-    right->container = ui_box_container(right, content, 0, 0, UI_BOX_VERTICAL);
-    
-    uic_obj_add(obj, right);
-    uic_obj_add(obj, left);
-    
-    return sidebar;
-}
-
-UIWIDGET ui_tabview(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    // create a simple frame as container widget
-    // when tabs are selected, the current child will be replaced by the
-    // the new tab widget
-    Arg args[16];
-    int n = 0;
-    XtSetArg(args[n], XmNshadowType, XmSHADOW_ETCHED_OUT);
-    n++;
-    XtSetArg(args[n], XmNshadowThickness, 0);
-    n++;
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    Widget form = XmCreateForm(parent, "tabview", args, n);
-    ct->add(ct, form);
-    XtManageChild(form);
-    
-    UiObject *tabviewobj = uic_object_new(obj, form);
-    tabviewobj->container = ui_tabview_container(obj, form);
-    uic_obj_add(obj, tabviewobj);
-    
-    XtVaSetValues(form, XmNuserData, tabviewobj->container, NULL);
-    
-    return form;
-}
-
-void ui_tab(UiObject *obj, char *title) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.label = title;
-    
-    ui_vbox(obj);
-}
-
-void ui_select_tab(UIWIDGET tabview, int tab) {
-    UiTabViewContainer *ct = NULL;
-    XtVaGetValues(tabview, XmNuserData, &ct, NULL);
-    if(ct) {
-        XtUnmanageChild(ct->current);
-        Widget w = cxListAt(ct->tabs, tab);
-        if(w) {
-            XtManageChild(w);
-            ct->current = w;
-        } else {
-            fprintf(stderr, "UiError: front tab index: %d\n", tab);
-        }
-    } else {
-        fprintf(stderr, "UiError: widget is not a tabview\n");
+Widget ui_vbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n) {
+    UiBoxContainer *box = (UiBoxContainer*)ctn;
+    int a = *n;
+    XtSetArg(args[a], gridRow, box->n); a++;
+    if(box->container.layout.fill == UI_ON) {
+        XtSetArg(args[a], gridVExpand, TRUE); a++;
+        XtSetArg(args[a], gridVFill, TRUE); a++;
     }
-}
-
-
-/* document tabview */
-
-static void ui_tabbar_resize(Widget widget, XtPointer udata, XtPointer cdata) {
-    MotifTabbedPane *v = (MotifTabbedPane*)udata;
-    
-    int width = 0;
-    int height = 0;
-    XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL);
-    int button_width = width / 4;
-    int x = 0;
-    CxIterator tabIterator = cxListIterator(v->tabs);
-    cx_foreach(UiTab*, tab, tabIterator) {
-        XtVaSetValues(
-                tab->tab_button,
-                XmNx, x,
-                XmNy, 0,
-                XmNwidth,
-                button_width,
-                
-                NULL);
-        x += button_width;
-    }
-    
-    if(height <= v->height) {
-        XtVaSetValues(widget, XmNheight, v->height + 4, NULL);
-    }
-}
-
-static void ui_tabbar_expose(Widget widget, XtPointer udata, XtPointer cdata) {
-    MotifTabbedPane *v = (MotifTabbedPane*)udata;
-    XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *)cdata;
-    XEvent *event = cbs->event;
-    Display *dpy = XtDisplay(widget); 
-    
-    XGCValues gcvals;
-    GC gc;
-    Pixel fgpix;
-    
-    int tab_x;
-    int tab_width;
-    XtVaGetValues(v->current->tab_button, XmNx, &tab_x, XmNwidth, &tab_width, XmNhighlightColor, &fgpix, NULL);
-    
-    gcvals.foreground = v->bg1;
-    gc = XCreateGC( dpy, XtWindow(widget), (GCForeground), &gcvals);
-      
-    int width = 0;
-    int height = 0;
-    XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL);
-    XFillRectangle(dpy, XtWindow(widget), gc, 0, 0, width, height);
-    
-    gcvals.foreground = fgpix;
-    gc = XCreateGC( dpy, XtWindow(widget), (GCForeground), &gcvals);
-    
-    XFillRectangle(dpy, XtWindow(widget), gc, tab_x, 0, tab_width, height);
-    
+    XtSetArg(args[a], gridHExpand, TRUE); a++;
+    XtSetArg(args[a], gridHFill, TRUE); a++;
+    *n = a;
+    return ui_box_container_prepare(box, args, n);
 }
 
-UiTabbedPane* ui_tabbed_document_view(UiObject *obj) {
-    int n = 0;
-    Arg args[16];
-    
-    UiContainer *ct = uic_get_current_container(obj);
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    
-    Widget tabview = XmCreateForm(parent, "tabview_form", args, n);
-    XtManageChild(tabview);
-    
-    XtSetArg(args[0], XmNorientation, XmHORIZONTAL);
-    XtSetArg(args[1], XmNpacking, XmPACK_TIGHT);
-    XtSetArg(args[2], XmNspacing, 1);
-    XtSetArg(args[3], XmNleftAttachment, XmATTACH_FORM);
-    XtSetArg(args[4], XmNrightAttachment, XmATTACH_FORM);
-    XtSetArg(args[5], XmNtopAttachment, XmATTACH_FORM);
-    XtSetArg(args[6], XmNmarginWidth, 0);
-    XtSetArg(args[7], XmNmarginHeight, 0);
-    Widget tabbar = XmCreateDrawingArea(tabview, "tabbar", args, 8);
-    XtManageChild(tabbar);
-    
-    XtSetArg(args[0], XmNleftAttachment, XmATTACH_FORM);
-    XtSetArg(args[1], XmNrightAttachment, XmATTACH_FORM);
-    XtSetArg(args[2], XmNtopAttachment, XmATTACH_WIDGET);
-    XtSetArg(args[3], XmNtopWidget, tabbar);
-    XtSetArg(args[4], XmNbottomAttachment, XmATTACH_FORM);
-    XtSetArg(args[5], XmNshadowThickness, 0);
-    Widget tabct = XmCreateForm(tabview, "tabview", args, 6);
-    XtManageChild(tabct);
-    
-    MotifTabbedPane *tabbedpane = ui_malloc(obj->ctx, sizeof(MotifTabbedPane));
-    tabbedpane->view.ctx = uic_current_obj(obj)->ctx;
-    tabbedpane->view.widget = tabct;
-    tabbedpane->view.document = NULL;
-    tabbedpane->tabbar = tabbar;
-    tabbedpane->tabs = cxArrayListCreate(obj->ctx->allocator, cx_cmp_uintptr, CX_STORE_POINTERS, 16);
-    tabbedpane->current = NULL;
-    tabbedpane->height = 0;
-    
-    XtAddCallback(tabbar, XmNresizeCallback , ui_tabbar_resize, tabbedpane);
-    XtAddCallback(tabbar, XmNexposeCallback, ui_tabbar_expose, tabbedpane);
-    
-    return &tabbedpane->view;
+Widget ui_hbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n) {
+    UiBoxContainer *box = (UiBoxContainer*)ctn;
+    int a = *n;
+    XtSetArg(args[a], gridRow, box->n); a++;
+    if(box->container.layout.fill == UI_ON) {
+        XtSetArg(args[a], gridHExpand, TRUE); a++;
+    }
+    XtSetArg(args[a], gridVExpand, TRUE); a++;
+    *n = a;
+    return ui_box_container_prepare(box, args, n);
 }
 
-UiObject* ui_document_tab(UiTabbedPane *view) {
-    MotifTabbedPane *v = (MotifTabbedPane*)view;
-    int n = 0;
-    Arg args[16];
-    
-    // hide the current tab content
-    if(v->current) {
-        XtUnmanageChild(v->current->content->widget);
-    }
-    
-    UiTab *tab = ui_malloc(view->ctx, sizeof(UiTab));
-    
-    // create the new tab content
-    XtSetArg(args[0], XmNshadowThickness, 0);
-    XtSetArg(args[1], XmNleftAttachment, XmATTACH_FORM);
-    XtSetArg(args[2], XmNrightAttachment, XmATTACH_FORM);
-    XtSetArg(args[3], XmNtopAttachment, XmATTACH_FORM);
-    XtSetArg(args[4], XmNbottomAttachment, XmATTACH_FORM);
-    XtSetArg(args[5], XmNuserData, tab);
-    Widget frame = XmCreateFrame(view->widget, "tab", args, 6);
-    XtManageChild(frame);
-    
-    UiObject *content = ui_malloc(view->ctx, sizeof(UiObject));
-    content->widget = NULL; // initialization for uic_context()
-    content->ctx = uic_context(content, view->ctx->mp);
-    content->ctx->parent = view->ctx;
-    content->ctx->attach_document = uic_context_attach_document;
-    content->ctx->detach_document2 = uic_context_detach_document2;
-    content->widget = frame;
-    content->window = view->ctx->obj->window;
-    content->container = ui_frame_container(content, frame);
-    content->next = NULL;
-    
-    // add tab button
-    cxListAdd(v->tabs, tab);
-    
-    XmString label = XmStringCreateLocalized("tab");
-    XtSetArg(args[0], XmNlabelString, label);
-    XtSetArg(args[1], XmNshadowThickness, 0);
-    XtSetArg(args[2], XmNhighlightThickness, 0);
-    
-    Widget button = XmCreatePushButton(v->tabbar, "tab_button", args, 3);
-    tab->tabbedpane = v;
-    tab->content = content;
-    tab->tab_button = button; 
-    XtManageChild(button);
-    XtAddCallback(
-        button,
-        XmNactivateCallback,
-        (XtCallbackProc)ui_tab_button_callback,
-        tab);
-    
-    if(v->height == 0) {
-        XtVaGetValues(
-                button,
-                XmNarmColor,
-                &v->bg1,
-                XmNbackground,
-                &v->bg2,
-                XmNheight,
-                &v->height,
-                NULL);
-        v->height += 2; // border
-    }
-    
-    ui_change_tab(v, tab);
-    ui_tabbar_resize(v->tabbar, v, NULL);
-    
-    return content;
-}
-
-void ui_tab_button_callback(Widget widget, UiTab *tab, XtPointer d) {  
-    MotifTabbedPane *t = tab->tabbedpane;
-    if(t->current) {
-        XtUnmanageChild(t->current->content->widget);
-        XtVaSetValues(t->current->tab_button, XmNset, 0, NULL);
-    }
-    XtManageChild(tab->content->widget);
-    
-    ui_change_tab(t, tab);
+void ui_box_container_add(UiContainerPrivate *ctn, Widget widget) {
+    ui_reset_layout(ctn->layout);
     
 }
-
-void ui_change_tab(MotifTabbedPane *pane, UiTab *tab) {
-    UiContext *ctx = tab->content->ctx;
-    ctx->parent->detach_document2(ctx->parent, pane->current->content->ctx->document);
-    ctx->parent->attach_document(ctx->parent, ctx->document);
-    
-    if(pane->current) {
-        XtVaSetValues(pane->current->tab_button, XmNshadowThickness, 0, XmNbackground, pane->bg1, NULL);
-    }
-    XtVaSetValues(tab->tab_button, XmNshadowThickness, 1, XmNbackground, pane->bg2, NULL);
-    
-    pane->current = tab;
-    pane->index = cxListFind(pane->tabs, tab);
-    printf("index: %d\n", pane->index);
-    
-    // redraw tabbar
-    Display *dpy = XtDisplay(pane->tabbar);
-    Window window = XtWindow(pane->tabbar);
-    if(dpy && window) {
-        XClearArea(dpy, XtWindow(pane->tabbar), 0, 0, 0, 0, TRUE);
-        XFlush(dpy);
-    }
-}
-
-void ui_tab_set_document(UiContext *ctx, void *document) {
-    if(ctx->parent->document) {
-        //ctx->parent->detach_document(ctx->parent, ctx->parent->document);
-    }
-    uic_context_attach_document(ctx, document);
-    //uic_context_set_document(ctx->parent, document);
-    //ctx->parent->document = document;
-    
-    UiTab *tab = NULL;
-    XtVaGetValues(
-            ctx->obj->widget,
-            XmNuserData,
-            &tab,
-            NULL);
-    if(tab) {
-        if(tab->tabbedpane->current == tab) {
-            ctx->parent->attach_document(ctx->parent, ctx->document);
-        }
-    } else {
-        fprintf(stderr, "UiError: ui_bar_set_document: Cannot set document");
-    }
-}
-
-
-
-/*
- * -------------------- Layout Functions --------------------
- * 
- * functions for setting layout attributes for the current container
- *
- */
-
-void ui_layout_fill(UiObject *obj, UiBool fill) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.fill = ui_bool2lb(fill);
-}
-
-void ui_layout_hexpand(UiObject *obj, UiBool expand) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.hexpand = expand;
-}
-
-void ui_layout_vexpand(UiObject *obj, UiBool expand) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.vexpand = expand;
-}
-
-void ui_layout_gridwidth(UiObject *obj, int width) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.gridwidth = width;
-}
-
-void ui_newline(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.newline = TRUE;
-}
--- a/ui/motif/container.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/container.h	Wed Dec 04 18:31:22 2024 +0100
@@ -37,26 +37,37 @@
 #ifdef	__cplusplus
 extern "C" {
 #endif
-
-#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout))
     
-typedef struct MotifTabbedPane    MotifTabbedPane;
-typedef struct UiTab              UiTab;
-typedef struct UiBoxContainer     UiBoxContainer;
-typedef struct UiGridContainer    UiGridContainer;
-typedef struct UiTabViewContainer UiTabViewContainer;
-typedef struct UiLayout           UiLayout;
+#define UI_APPLY_LAYOUT(layout, args) \
+    layout.fill = args.fill; \
+    layout.hexpand = args.hexpand; \
+    layout.vexpand = args.vexpand; \
+    layout.hfill = args.hfill; \
+    layout.vfill = args.vfill; \
+    layout.colspan = args.colspan; \
+    layout.rowspan = args.rowspan
+    
+typedef enum UiBoxOrientation UiBoxOrientation;
+    
+#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout))
+#define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE)
+#define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE)
 
-typedef Widget (*ui_container_add_f)(UiContainer*, Arg*, int*, UiBool);
-
-typedef enum UiLayoutBool     UiLayoutBool;
-typedef enum UiBoxOrientation UiBoxOrientation;
+#define ui_obj_container(obj) (UiContainerPrivate*)obj->container_end
+    
+typedef struct UiLayout UiLayout;
 
-
-enum UiLayoutBool {
-    UI_LAYOUT_UNDEFINED = 0,
-    UI_LAYOUT_TRUE,
-    UI_LAYOUT_FALSE,
+struct UiLayout {
+    UiTri        fill;
+    UiBool       newline;
+    char         *label;
+    UiBool       hexpand;
+    UiBool       vexpand;
+    UiBool       hfill;
+    UiBool       vfill;
+    int          width;
+    int          colspan;
+    int          rowspan;
 };
 
 enum UiBoxOrientation {
@@ -64,93 +75,32 @@
     UI_BOX_HORIZONTAL
 };
 
-struct UiLayout {
-    UiLayoutBool fill;
-    UiBool       newline;
-    char         *label;
-    UiBool       hexpand;
-    UiBool       vexpand;
-    int          gridwidth;
-};
+typedef struct UiContainerPrivate UiContainerPrivate;
 
-struct UiContainer {
-    Widget   widget;
-    Widget   (*prepare)(UiContainer*, Arg *, int*, UiBool);
-    void     (*add)(UiContainer*, Widget);
-    UiLayout layout;
-    Widget   current;
-    Widget   menu;
-};
 
-struct UiBoxContainer {
-    UiContainer container;
-    Widget      prev_widget;
-    UiBool      has_fill;
-    UiBoxOrientation orientation;
-    int         margin;
-    int         spacing;
-};
-
-struct UiGridContainer {
-    UiContainer container;
-    CxList      *lines;
-    CxList      *current;
-    int         columnspacing;
-    int         rowspacing;
-};
-
-struct UiTabViewContainer {
-    UiContainer container;
-    UiContext   *context;
-    Widget      widget;
-    CxList      *tabs;
-    Widget      current;
+struct UiContainerPrivate {
+    UiContainerX container;
+    Widget       (*prepare)(UiContainerPrivate*, Arg *, int*);
+    void         (*add)(UiContainerPrivate*, Widget);
+    Widget       widget;
+    UiLayout     layout;
 };
 
-struct MotifTabbedPane {
-    UiTabbedPane view;
-    Widget       tabbar;
-    CxList       *tabs;
-    UiTab        *current;
-    int          index;
-    Pixel        bg1;
-    Pixel        bg2;
-    int          height;
-};
-
-struct UiTab {
-    MotifTabbedPane *tabbedpane;
-    UiObject        *content;
-    Widget          tab_button;
-};
-    
-
-UiContainer* ui_frame_container(UiObject *obj, Widget frame);
-Widget ui_frame_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill);
-void ui_frame_container_add(UiContainer *ct, Widget widget);
+typedef struct UiBoxContainer {
+    UiContainerPrivate container;
+    Dimension n;
+} UiBoxContainer;
 
-UiContainer* ui_box_container(UiObject *obj, Widget box, int margin, int spacing, UiBoxOrientation orientation);
-Widget ui_box_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill);
-void ui_box_container_add(UiContainer *ct, Widget widget);
-
-UiContainer* ui_grid_container(UiObject *obj, Widget form, int columnspacing, int rowspacing);
-Widget ui_grid_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill);
-void ui_grid_container_add(UiContainer *ct, Widget widget);
+typedef struct UiGridContainer {
+    UiContainerPrivate container;
+    int x;
+    int y;
+} UiGridContainer;
 
-UiContainer* ui_scrolledwindow_container(UiObject *obj, Widget scrolledwindow);
-Widget ui_scrolledwindow_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill);
-void ui_scrolledwindow_container_add(UiContainer *ct, Widget widget);
-
-UiContainer* ui_tabview_container(UiObject *obj, Widget rowcolumn);
-Widget ui_tabview_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill);
-void ui_tabview_container_add(UiContainer *ct, Widget widget);
-
-void ui_tab_button_callback(Widget widget, UiTab *tab, XtPointer d);
-void ui_change_tab(MotifTabbedPane *pane, UiTab *tab);
-
-void ui_tab_set_document(UiContext *ctx, void *document);
-void ui_tab_detach_document(UiContext *ctx);
-
+UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orientation);
+Widget ui_vbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n);
+Widget ui_hbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n);
+void ui_box_container_add(UiContainerPrivate *ctn, Widget widget);
 
 #ifdef	__cplusplus
 }
--- a/ui/motif/dnd.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/dnd.c	Wed Dec 04 18:31:22 2024 +0100
@@ -28,18 +28,3 @@
 
 #include "dnd.h"
 
-void ui_selection_settext(UiSelection *sel, char *str, int len) {
-    
-}
-
-void ui_selection_seturis(UiSelection *sel, char **uris, int nelm) {
-    
-}
-
-char* ui_selection_gettext(UiSelection *sel) {
-    return NULL;
-}
-
-char** ui_selection_geturis(UiSelection *sel, size_t *nelm) {
-    return NULL;
-}
--- a/ui/motif/graphics.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/graphics.c	Wed Dec 04 18:31:22 2024 +0100
@@ -34,250 +34,3 @@
 #include "graphics.h"
 
 #include "container.h"
-
-static void ui_drawingarea_expose(Widget widget, XtPointer u, XtPointer c) {
-    UiDrawEvent *drawevent = u;
-    //XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *)c;
-    //XEvent *event = cbs->event;
-    Display *dpy = XtDisplay(widget);
-    
-    UiEvent ev;
-    ev.obj = drawevent->obj;
-    ev.window = drawevent->obj->window;
-    ev.document = drawevent->obj->ctx->document;
-    ev.eventdata = NULL;
-    ev.intval = 0;
-    
-    XtVaGetValues(
-            widget,
-            XmNwidth,
-            &drawevent->gr.g.width,
-            XmNheight,
-            &drawevent->gr.g.height,
-            NULL);
-    
-    XGCValues gcvals;
-    gcvals.foreground = BlackPixelOfScreen(XtScreen(widget));
-    drawevent->gr.gc = XCreateGC(dpy, XtWindow(widget), (GCForeground), &gcvals);
-    
-    drawevent->callback(&ev, &drawevent->gr.g, drawevent->userdata);
-}
-
-UIWIDGET ui_drawingarea(UiObject *obj, ui_drawfunc f, void *userdata) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    int n = 0;
-    Arg args[16];
-    
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    Widget drawingarea = XmCreateDrawingArea(parent, "drawingarea", args, n);
-    
-    if(f) {
-        UiDrawEvent *event = malloc(sizeof(UiDrawEvent));
-        event->obj = obj;
-        event->callback = f;
-        event->userdata = userdata;
-        
-        event->gr.display = XtDisplay(drawingarea);
-        event->gr.widget = drawingarea;
-        
-        Colormap colormap;
-        XtVaGetValues(drawingarea, XmNcolormap, &colormap, NULL);    
-        event->gr.colormap = colormap;
-        
-        XtAddCallback(
-                drawingarea,
-                XmNexposeCallback,
-                ui_drawingarea_expose,
-                event);
-        
-        XtVaSetValues(drawingarea, XmNuserData, event, NULL);
-    }
-    
-    XtManageChild(drawingarea);
-    return drawingarea;
-}
-
-static void ui_drawingarea_input(Widget widget, XtPointer u, XtPointer c) {
-    XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c;
-    XEvent *xevent = cbs->event;
-    UiMouseEventData *event = u;
-    
-    if (cbs->reason == XmCR_INPUT) {
-        if (xevent->xany.type == ButtonPress) {
-            UiMouseEvent me;
-            me.x = xevent->xbutton.x;
-            me.y = xevent->xbutton.y;
-            // TODO: configurable double click time
-            me.type = xevent->xbutton.time - event->last_event > 300 ? UI_PRESS : UI_PRESS2;
-            
-            UiEvent e;
-            e.obj = event->obj;
-            e.window = event->obj->window;
-            e.document = event->obj->ctx->document;
-            e.eventdata = &me;
-            e.intval = 0;
-            event->callback(&e, event->userdata);
-            
-            
-            event->last_event = me.type == UI_PRESS2 ? 0 : xevent->xbutton.time;
-        }
-    }
-    
-}
-
-void ui_drawingarea_mousehandler(UiObject *obj, UIWIDGET widget, ui_callback f, void *u) {
-    if(f) {
-        UiMouseEventData *event = malloc(sizeof(UiMouseEventData));
-        event->obj = obj;
-        event->callback = f;
-        event->userdata = u;
-        event->last_event = 0;
-        
-        XtAddCallback(widget, XmNinputCallback, ui_drawingarea_input, event);
-    }
-}
-
-void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height) {
-    XtVaGetValues(
-            drawingarea,
-            XmNwidth,
-            width,
-            XmNheight,
-            height,
-            NULL);
-}
-
-void ui_drawingarea_redraw(UIWIDGET drawingarea) {
-    //XClearArea(XtDisplay(drawingarea), drawingarea->core.window, 0, 0, drawingarea->core.width, drawingarea->core.height, True);
-    UiDrawEvent *event;
-    XtVaGetValues(drawingarea, XmNuserData, &event, NULL);
-    ui_drawingarea_expose(drawingarea, event, NULL);
-}
-
-
-/* -------------------- text layout functions -------------------- */
-UiTextLayout* ui_text(UiGraphics *g) {
-    UiTextLayout *text = malloc(sizeof(UiTextLayout));
-    memset(text, 0, sizeof(UiTextLayout));
-    text->text = NULL;
-    text->length = 0;
-    text->widget = ((UiXlibGraphics*)g)->widget;
-    text->fontset = NULL;
-    return text;
-}
-
-static void create_default_fontset(UiTextLayout *layout) {
-    char **missing = NULL;
-    int num_missing = 0;
-    char *def = NULL;
-    Display *dpy = XtDisplay(layout->widget);
-    XFontSet fs = XCreateFontSet(
-        dpy,
-        "-dt-interface system-medium-r-normal-s*utf*:,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-1,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-10,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-15,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-2,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-3,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-4,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-5,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-9,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-e,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-r,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-ru,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-u,"
-                "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-uni,"
-                "-misc-fixed-medium-r-normal--14-130-75-75-c-140-jisx0208",
-        &missing, &num_missing, &def);
-    layout->fontset = fs;
-}
-
-void ui_text_free(UiTextLayout *text) {
-    // TODO
-}
-
-void ui_text_setstring(UiTextLayout *layout, char *str) {
-    ui_text_setstringl(layout, str, strlen(str));
-}
-
-void ui_text_setstringl(UiTextLayout *layout, char *str, int len) {
-    layout->text = str;
-    layout->length = len;
-    layout->changed = 1;
-}
-
-void ui_text_setfont(UiTextLayout *layout, char *font, int size) {
-    create_default_fontset(layout);//TODO
-    layout->changed = 1;
-}
-
-void ui_text_getsize(UiTextLayout *layout, int *width, int *height) {
-    if(layout->changed) {
-        XRectangle ext, lext;
-        XmbTextExtents(layout->fontset, layout->text, layout->length, &ext, &lext);
-        layout->width = ext.width;
-        layout->height = ext.height;
-        layout->changed = 0;
-    }
-    *width = layout->width;
-    *height = layout->height;
-}
-
-void ui_text_setwidth(UiTextLayout *layout, int width) {
-    layout->maxwidth = width;
-}
-
-
-/* -------------------- drawing functions -------------------- */
-
-void ui_graphics_color(UiGraphics *g, int red, int green, int blue) {
-    UiXlibGraphics *gr = (UiXlibGraphics*)g;
-    XColor color;
-    color.flags= DoRed | DoGreen | DoBlue; 
-    color.red = red * 257;
-    color.green = green * 257;
-    color.blue = blue * 257;
-    XAllocColor(gr->display, gr->colormap, &color);
-    XSetForeground(gr->display, gr->gc, color.pixel);
-}
-
-void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2) {
-    UiXlibGraphics *gr = (UiXlibGraphics*)g;
-    XDrawLine(gr->display, XtWindow(gr->widget), gr->gc, x1, y1, x2, y2);
-}
-
-void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, int fill) {
-    UiXlibGraphics *gr = (UiXlibGraphics*)g;
-    if(fill) {
-        XFillRectangle(gr->display, XtWindow(gr->widget), gr->gc, x, y, w, h);
-    } else {
-        XDrawRectangle(gr->display, XtWindow(gr->widget), gr->gc, x, y, w, h);
-    }
-}
-
-void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text) {
-    UiXlibGraphics *gr = (UiXlibGraphics*)g;
-    int width, height;
-    ui_text_getsize(text, &width, &height);
-    if(text->maxwidth > 0) {
-        XRectangle clip;
-        clip.x = x;
-        clip.y = y;
-        clip.width = text->maxwidth;
-        clip.height = height;
-        XSetClipRectangles(gr->display, gr->gc, 0, 0, &clip, 1, Unsorted);
-    }
-    
-    XmbDrawString(
-            gr->display,
-            XtWindow(gr->widget),
-            text->fontset,
-            gr->gc,
-            x,
-            y + height,
-            text->text,
-            text->length);
-    
-    XSetClipMask(gr->display, gr->gc, None);
-}
--- a/ui/motif/graphics.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/graphics.h	Wed Dec 04 18:31:22 2024 +0100
@@ -36,38 +36,6 @@
 extern "C" {
 #endif
 
-typedef struct UiXlibGraphics {
-    UiGraphics g;
-    Display    *display;
-    Widget     widget;
-    Colormap   colormap;
-    GC         gc;
-} UiXlibGraphics;
-
-typedef struct UiDrawEvent {
-    ui_drawfunc    callback;
-    UiObject       *obj;
-    void           *userdata;
-    UiXlibGraphics gr;
-} UiDrawEvent;
-
-typedef struct UiMouseEventData {
-    UiObject    *obj;
-    ui_callback callback;
-    void        *userdata;
-    Time        last_event;
-} UiMouseEventData;
-
-struct UiTextLayout {
-    char     *text;
-    size_t   length;
-    Widget   widget;
-    XFontSet fontset;
-    int      maxwidth;
-    int      width;
-    int      height;
-    int      changed;
-};
 
 
 #ifdef	__cplusplus
--- a/ui/motif/image.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/image.c	Wed Dec 04 18:31:22 2024 +0100
@@ -6,35 +6,3 @@
 
 #include "image.h"
 
-UiIcon* ui_icon(const char *name, int size) {
-    return NULL;
-}
-
-UiIcon* ui_icon_unscaled(const char *name, int size) {
-    return NULL;
-}
-
-void ui_free_icon(UiIcon *icon) {
-    
-}
-
-UiImage* ui_icon_image(UiIcon *icon) {
-    return NULL;
-}
-
-UiImage* ui_image(const char *filename) {
-    return NULL;
-}
-
-UiImage* ui_named_image(const char *filename, const char *name) {
-    return NULL;
-}
-
-UiImage* ui_load_image_from_path(const char *path, const char *name) {
-    return NULL;
-}
-
-void ui_free_image(UiImage *img) {
-    
-}
-
--- a/ui/motif/label.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/label.c	Wed Dec 04 18:31:22 2024 +0100
@@ -34,36 +34,3 @@
 #include "../common/context.h"
 #include "../common/object.h"
 
-UIWIDGET ui_label(UiObject *obj, char *label) {
-    UiContainer *ct = uic_get_current_container(obj);
-    XmString str = XmStringCreateLocalized(label);
-    
-    int n = 0;
-    Arg args[16]; 
-    XtSetArg(args[n], XmNlabelString, str);
-    n++;
-    
-    Widget parent = ct->prepare(ct, args, &n, FALSE);
-    Widget widget = XmCreateLabel(parent, "label", args, n);
-    ct->add(ct, widget);  
-    XtManageChild(widget);
-    
-    return widget;
-}
-
-UIWIDGET ui_space(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
-    XmString str = XmStringCreateLocalized("");
-    
-    int n = 0;
-    Arg args[16]; 
-    XtSetArg(args[n], XmNlabelString, str);
-    n++;
-    
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    Widget widget = XmCreateLabel(parent, "space_label", args, n);
-    ct->add(ct, widget);  
-    XtManageChild(widget);
-    
-    return widget;
-}
--- a/ui/motif/list.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/list.c	Wed Dec 04 18:31:22 2024 +0100
@@ -34,175 +34,3 @@
 #include "list.h"
 #include "../common/object.h"
 
-
-void* ui_strmodel_getvalue(void *elm, int column) {
-    return column == 0 ? elm : NULL;
-}
-
-
-UIWIDGET ui_listview_str(UiObject *obj, UiList *list, ui_callback f, void *udata) {
-    return ui_listview(obj, list, ui_strmodel_getvalue, f, udata);
-}
-
-UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
-    int count;
-    XmStringTable items = ui_create_stringlist(var->value, getvalue, &count);
-    
-    Arg args[8];
-    int n = 0;
-    XtSetArg(args[n], XmNitemCount, count);
-    n++;
-    XtSetArg(args[n], XmNitems, count == 0 ? NULL : items);
-    n++;
-    
-    UiContainer *ct = uic_get_current_container(obj);
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    Widget widget = XmCreateScrolledList(parent, "listview", args, n);
-    ct->add(ct, XtParent(widget));
-    XtManageChild(widget);
-    
-    UiListView *listview = cxMalloc(obj->ctx->allocator, sizeof(UiListView));
-    listview->widget = widget;
-    listview->list = var;
-    listview->getvalue = getvalue;
-    
-    for (int i=0;i<count;i++) {
-        XmStringFree(items[i]);
-    }
-    XtFree((char *)items);
-    
-    if(f) {
-        UiListViewEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiListViewEventData));
-        event->event.obj = obj;
-        event->event.userdata = udata;
-        event->event.callback = f;
-        event->event.value = 0;
-        event->var = var;
-        XtAddCallback(
-                widget,
-                XmNdefaultActionCallback,
-                (XtCallbackProc)ui_list_selection_callback,
-                event);
-    }
-    
-    return widget;
-}
-
-UIWIDGET ui_listview(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = list;
-    var->type = UI_VAR_SPECIAL;
-    return ui_listview_var(obj, var, getvalue, f, udata);
-}
-
-UIWIDGET ui_listview_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST);
-    if(var) {
-        UiListVar *value = var->value;
-        return ui_listview_var(obj, var, getvalue, f, udata);
-    } else {
-        // TODO: error
-    }
-    return NULL;
-}
-
-
-XmStringTable ui_create_stringlist(UiList *list, ui_getvaluefunc getvalue, int *count) { 
-    int num = list->count(list);
-    XmStringTable items = (XmStringTable)XtMalloc(num * sizeof(XmString));
-    void *data = list->first(list);
-    for(int i=0;i<num;i++) {
-        items[i] = XmStringCreateLocalized(getvalue(data, 0));
-        data = list->next(list);
-    }
-    
-    *count = num;
-    return items;
-}
-
-
-void ui_listview_update(UiEvent *event, UiListView *view) {
-    int count;
-    XmStringTable items = ui_create_stringlist(
-            view->list->value,
-            view->getvalue,
-            &count);
-    
-    XtVaSetValues(
-            view->widget,
-            XmNitems, count == 0 ? NULL : items,
-            XmNitemCount,
-            count,
-            NULL);
-    
-    for (int i=0;i<count;i++) {
-        XmStringFree(items[i]);
-    }
-    XtFree((char *)items);
-}
-
-void ui_list_selection_callback (Widget widget, UiListViewEventData *event, XtPointer data) {
-    XmListCallbackStruct *cbs = (XmListCallbackStruct *)data;
-    
-    UiEvent e;
-    e.obj = event->event.obj;
-    e.window = event->event.obj->window;
-    e.document = event->event.obj->ctx->document;
-    UiList *list = event->var->value;
-    e.eventdata = list->get(list, cbs->item_position - 1);
-    e.intval = cbs->item_position - 1;
-    event->event.callback(&e, event->event.userdata);
-}
-
-
-/* --------------------------- ComboBox ---------------------------  */
-
-UIWIDGET ui_combobox_str(UiObject *obj, UiList *list, ui_callback f, void *udata) {
-    return ui_combobox(obj, list, ui_strmodel_getvalue, f, udata);
-}
-
-UIWIDGET ui_combobox(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = list;
-    var->type = UI_VAR_SPECIAL;
-    return ui_combobox_var(obj, var, getvalue, f, udata);
-}
-
-UIWIDGET ui_combobox_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST);
-    if(var) {
-        UiListVar *value = var->value;
-        return ui_combobox_var(obj, var, getvalue, f, udata);
-    } else {
-        // TODO: error
-    }
-    return NULL;
-}
-
-UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
-    UiListView *listview = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiListView));
-    
-    UiContainer *ct = uic_get_current_container(obj);
-    Arg args[16];
-    int n = 0;
-    XtSetArg(args[n], XmNindicatorOn, XmINDICATOR_NONE);
-    n++;
-    XtSetArg(args[n], XmNtraversalOn, FALSE);
-    n++;
-    XtSetArg(args[n], XmNwidth, 160);
-    n++;
-    Widget parent = ct->prepare(ct, args, &n, FALSE);
-    Widget combobox = XmCreateDropDownList(parent, "combobox", args, n);
-    XtManageChild(combobox);
-    listview->widget = combobox;
-    listview->list = var;
-    listview->getvalue = getvalue;
-    
-    ui_listview_update(NULL, listview);
-    
-    return parent;
-}
--- a/ui/motif/list.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/list.h	Wed Dec 04 18:31:22 2024 +0100
@@ -37,24 +37,7 @@
 extern "C" {
 #endif
 
-typedef struct UiListView {
-    Widget          widget;
-    UiVar           *list;
-    ui_getvaluefunc getvalue;
-} UiListView;
-
-typedef struct UiListViewEventData {
-    UiEventData event;
-    UiVar *var;
-} UiListViewEventData;
-
-void* ui_strmodel_getvalue(void *elm, int column);
-
-XmStringTable ui_create_stringlist(UiList *list, ui_getvaluefunc getvalue, int *count);
-void ui_listview_update(UiEvent *event, UiListView *view);
-void ui_list_selection_callback (Widget widget, UiListViewEventData *event, XtPointer data);
-
-UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata);
+    
 
 #ifdef	__cplusplus
 }
--- a/ui/motif/menu.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/menu.c	Wed Dec 04 18:31:22 2024 +0100
@@ -41,444 +41,3 @@
 #include <cx/linked_list.h>
 #include <cx/array_list.h>
 
-static ui_menu_add_f createMenuItem[] = {
-    /* UI_MENU                 */ add_menu_widget,
-    /* UI_MENU_SUBMENU         */ add_menu_widget,
-    /* UI_MENU_ITEM            */ add_menuitem_widget,
-    /* UI_MENU_STOCK_ITEM      */ add_menuitem_st_widget,
-    /* UI_MENU_CHECK_ITEM      */ add_checkitem_widget,
-    /* UI_MENU_CHECK_ITEM_NV   */ add_checkitemnv_widget,
-    /* UI_MENU_ITEM_LIST       */ add_menuitem_list_widget,
-    /* UI_MENU_ITEM_LIST_NV    */ NULL, // TODO
-    /* UI_MENU_SEPARATOR       */ add_menuseparator_widget
-};
-
-// private menu functions
-void ui_create_menubar(UiObject *obj) {
-    UiMenu *menus = uic_get_menu_list();
-    if(!menus) {
-        return;
-    }
-    
-    Widget menubar = XmCreateMenuBar(obj->widget, "main_list", NULL, 0);
-    XtManageChild(menubar);
-    
-    UiMenu *menu = menus;
-    int menu_index = 0;
-    while(menu) {
-        menu_index += add_menu_widget(menubar, menu_index, &menu->item, obj);
-        
-        menu = (UiMenu*)menu->item.next;
-    }
-}
-
-int add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) {
-    UiMenu *menu = (UiMenu*)item;
-    
-    Widget menuItem = XtVaCreateManagedWidget(
-            menu->label,
-            xmCascadeButtonWidgetClass,
-            parent,
-            NULL);
-    Widget m = XmVaCreateSimplePulldownMenu(parent, menu->label, i, NULL, NULL);
-    
-    UiMenuItemI *mi = menu->items_begin;
-    int menu_index = 0;
-    while(mi) {
-        menu_index += createMenuItem[mi->type](m, menu_index, mi, obj);
-        mi = mi->next;
-    }
-    
-    return 1;
-}
-
-int add_menuitem_widget(
-        Widget parent,
-        int i,
-        UiMenuItemI *item,
-        UiObject *obj)
-{
-    UiMenuItem *mi = (UiMenuItem*)item;
-    
-    Arg args[1];
-    XmString label = XmStringCreateLocalized(mi->label);
-    XtSetArg(args[0], XmNlabelString, label);
-    
-    Widget mitem = XtCreateManagedWidget(
-            "menubutton",
-            xmPushButtonWidgetClass,
-            parent,
-            args,
-            1);
-    XmStringFree(label);
-    
-    if(mi->callback != NULL) {
-        UiEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = mi->userdata;
-        event->callback = mi->callback;
-        event->value = 0;
-        XtAddCallback(
-                mitem,
-                XmNactivateCallback,
-                (XtCallbackProc)ui_push_button_callback,
-                event);
-    }
-    
-    if(mi->groups) {
-        uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups);
-    }
-    
-    return 1;
-}
-
-int add_menuitem_st_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) {
-    UiStMenuItem *mi = (UiStMenuItem*)item;
-    
-    UiStockItem *si = ui_get_stock_item(mi->stockid);
-    if(!si) {
-        fprintf(stderr, "UI Error: unknown stock id: %s\n", mi->stockid);
-        return 0;
-    }
-    
-    int n = 0;
-    Arg args[4];
-    XmString label = XmStringCreateLocalized(si->label);
-    XmString at = NULL;
-    
-    XtSetArg(args[n], XmNlabelString, label);
-    n++;
-    if(si->accelerator) {
-        XtSetArg(args[n], XmNaccelerator, si->accelerator);
-        n++;
-    }
-    if(si->accelerator_label) {
-        at = XmStringCreateLocalized(si->accelerator_label);
-        XtSetArg(args[n], XmNacceleratorText, at);
-        n++;
-    }
-    
-    Widget mitem = XtCreateManagedWidget(
-            "menubutton",
-            xmPushButtonWidgetClass,
-            parent,
-            args,
-            n);
-    XmStringFree(label);
-    if(at) {
-        XmStringFree(at);
-    }
-    
-    if(mi->callback != NULL) {
-        UiEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = mi->userdata;
-        event->callback = mi->callback;
-        event->value = 0;
-        XtAddCallback(
-                mitem,
-                XmNactivateCallback,
-                (XtCallbackProc)ui_push_button_callback,
-                event);
-    }
-    
-    if(mi->groups) {
-        uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups);
-    }
-    
-    return 1;
-}
-
-int add_menuseparator_widget(
-        Widget parent,
-        int i,
-        UiMenuItemI *item,
-        UiObject *obj)
-{
-    Widget s = XmCreateSeparatorGadget (parent, "menu_separator", NULL, 0);
-    XtManageChild(s);
-    return 1;
-}
-
-int add_checkitem_widget(
-        Widget parent,
-        int i,
-        UiMenuItemI *item,
-        UiObject *obj)
-{
-    UiCheckItem *ci = (UiCheckItem*)item;
-    
-    Arg args[3];
-    XmString label = XmStringCreateLocalized(ci->label);
-    XtSetArg(args[0], XmNlabelString, label);
-    XtSetArg(args[1], XmNvisibleWhenOff, 1);
-    Widget checkbox = XtCreateManagedWidget(
-            "menutogglebutton",
-            xmToggleButtonWidgetClass,
-            parent,
-            args,
-            2);
-    XmStringFree(label);
-    
-    if(ci->callback) {
-        UiEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = ci->userdata;
-        event->callback = ci->callback;
-        XtAddCallback(
-            checkbox,
-            XmNvalueChangedCallback,
-            (XtCallbackProc)ui_toggle_button_callback,
-            event);
-    }
-    
-    return 1;
-}
-
-int add_checkitemnv_widget(
-        Widget parent,
-        int i,
-        UiMenuItemI *item,
-        UiObject *obj)
-{
-    UiCheckItemNV *ci = (UiCheckItemNV*)item;
-    
-    Arg args[3];
-    XmString label = XmStringCreateLocalized(ci->label);
-    XtSetArg(args[0], XmNlabelString, label);
-    XtSetArg(args[1], XmNvisibleWhenOff, 1);
-    Widget checkbox = XtCreateManagedWidget(
-            "menutogglebutton",
-            xmToggleButtonWidgetClass,
-            parent,
-            args,
-            2);
-    XmStringFree(label);
-    
-    UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER);
-    if(var) {
-        UiInteger *value = var->value;
-        value->obj = checkbox;
-        value->get = ui_toggle_button_get;
-        value->set = ui_toggle_button_set;
-        value = 0;
-    } else {
-        // TODO: error
-    }
-    
-    return 1;
-}
-
-int add_menuitem_list_widget(
-        Widget parent,
-        int i,
-        UiMenuItemI *item,
-        UiObject *obj)
-{
-    UiMenuItemList *il = (UiMenuItemList*)item;
-    
-    UiActiveMenuItemList *ls = cxMalloc(
-            obj->ctx->allocator,
-            sizeof(UiActiveMenuItemList));
-    
-    ls->object = obj;
-    ls->menu = parent;
-    ls->index = i;
-    ls->oldcount = 0;
-    ls->list = il->list;
-    ls->callback = il->callback;
-    ls->userdata = il->userdata;
-    
-    ls->list->observers = ui_add_observer(
-            ls->list->observers,
-            (ui_callback)ui_update_menuitem_list,
-            ls);
-    
-    ui_update_menuitem_list(NULL, ls);
-    
-    return 0;
-}
-
-void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) {
-    Arg args[4];
-    
-    // remove old items
-    if(list->oldcount > 0) {
-        Widget *children;
-        int nc;
-        
-        XtVaGetValues(
-                list->menu,
-                XmNchildren,
-                &children,
-                XmNnumChildren,
-                &nc,
-                NULL);
-        
-        for(int i=0;i<list->oldcount;i++) {
-            XtDestroyWidget(children[list->index + i]);
-        }
-    }
-    
-    char *str = ui_list_first(list->list);
-    if(str) {
-        // add separator
-        XtSetArg(args[0], XmNpositionIndex, list->index);
-        Widget s = XmCreateSeparatorGadget (list->menu, "menu_separator", args, 1);
-        XtManageChild(s);
-    }
-    int i = 1;
-    while(str) {
-        XmString label = XmStringCreateLocalized(str);
-        XtSetArg(args[0], XmNlabelString, label);
-        XtSetArg(args[1], XmNpositionIndex, list->index + i);
-
-        Widget mitem = XtCreateManagedWidget(
-                "menubutton",
-                xmPushButtonWidgetClass,
-                list->menu,
-                args,
-                2);
-        XmStringFree(label);
-        
-        if(list->callback) {
-            // TODO: use mempool
-            UiEventData *event = malloc(sizeof(UiEventData));
-            event->obj = list->object;
-            event->userdata = list->userdata;
-            event->callback = list->callback;
-            event->value = i - 1;
-
-            XtAddCallback(
-                mitem,
-                XmNactivateCallback,
-                (XtCallbackProc)ui_push_button_callback,
-                event);
-        }
-        
-        str = ui_list_next(list->list);
-        i++;
-    }
-    
-    list->oldcount = i;
-}
-
-void ui_menu_event_wrapper(Widget widget, XtPointer udata, XtPointer cdata) {
-    UiEventData *event = udata;
-    UiEvent e;
-    e.obj = event->obj;
-    e.window = event->obj->window;
-    e.document = event->obj->ctx->document;
-    e.intval = 0;
-    event->callback(&e, event->userdata);    
-}
-
-
-/*
- * widget menu functions
- */
-
-static void ui_popup_handler(Widget widget, XtPointer data,  XEvent *event, Boolean *c) {
-    Widget menu = data;
-    XmMenuPosition(menu, (XButtonPressedEvent *)event);
-    XtManageChild(menu);
-    
-    *c = FALSE;
-}
-
-UIMENU ui_contextmenu(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
-    if(ct->current) {
-        return ui_contextmenu_w(obj, ct->current);
-    } else {
-        return NULL; // TODO: warn
-    }
-}
-
-UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    Widget menu = XmCreatePopupMenu(widget, "popup_menu", NULL, 0);
-    ct->menu = menu;
-    
-    XtAddEventHandler(widget, ButtonPressMask, FALSE, ui_popup_handler, menu);
-    
-    return menu;
-}
-
-void ui_contextmenu_popup(UIMENU menu) {
-    
-}
-
-void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata) {
-    ui_widget_menuitem_gr(obj, label, f, userdata, -1);
-}
-
-void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...) {
-    UiContainer *ct = uic_get_current_container(obj);
-    if(!ct->menu) {
-        return;
-    }
-    
-    // add groups
-    CxList *groups = NULL;
-    va_list ap;
-    va_start(ap, userdata);
-    int group;
-    while((group = va_arg(ap, int)) != -1) {
-        if(!groups) {
-            groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
-        }
-        cxListAdd(groups, &group);
-    }
-    va_end(ap);
-    
-    // create menuitem
-    Arg args[4];
-    XmString labelstr = XmStringCreateLocalized(label);
-    XtSetArg(args[0], XmNlabelString, labelstr);
-    
-    Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1);
-    XtManageChild(item);
-    XmStringFree(labelstr);
-}
-
-void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata) {
-    ui_widget_menuitem_stgr(obj, stockid, f, userdata, -1);
-}
-
-void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...) {
-    UiContainer *ct = uic_get_current_container(obj);
-    if(!ct->menu) {
-        return;
-    }
-    
-    // add groups
-    CxList *groups = NULL;
-    va_list ap;
-    va_start(ap, userdata);
-    int group;
-    while((group = va_arg(ap, int)) != -1) {
-        if(!groups) {
-            groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
-        }
-        cxListAdd(groups, &group);
-    }
-    va_end(ap);
-    
-    // create menuitem
-    UiStockItem *stockItem = ui_get_stock_item(stockid);
-    Arg args[4];
-    XmString labelstr = XmStringCreateLocalized(stockItem->label);
-    XtSetArg(args[0], XmNlabelString, labelstr);
-    
-    Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1);
-    XtManageChild(item);
-    XmStringFree(labelstr);
-}
--- a/ui/motif/menu.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/menu.h	Wed Dec 04 18:31:22 2024 +0100
@@ -37,33 +37,6 @@
 #endif
 
 
-typedef struct UiActiveMenuItemList UiActiveMenuItemList;
-
-typedef int(*ui_menu_add_f)(Widget, int, UiMenuItemI*, UiObject*);
-
-struct UiActiveMenuItemList {
-    UiObject     *object;
-    Widget       menu;
-    int          index;
-    int          oldcount;
-    UiList       *list;
-    ui_callback  callback;
-    void         *userdata;
-};
-
-void ui_create_menubar(UiObject *obj);
-
-int add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
-int add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
-int add_menuitem_st_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
-int add_menuseparator_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
-int add_checkitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
-int add_checkitemnv_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
-int add_menuitem_list_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj);
-
-void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list);
-void ui_menu_event_wrapper(Widget widget, XtPointer udata, XtPointer cdata);
-
 
 #ifdef	__cplusplus
 }
--- a/ui/motif/objs.mk	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/objs.mk	Wed Dec 04 18:31:22 2024 +0100
@@ -39,11 +39,11 @@
 MOTIFOBJ += label.o
 MOTIFOBJ += text.o
 MOTIFOBJ += list.o
-MOTIFOBJ += tree.o
 MOTIFOBJ += graphics.o
 MOTIFOBJ += range.o
 MOTIFOBJ += dnd.o
 MOTIFOBJ += image.o
+MOTIFOBJ += Grid.o
 
 TOOLKITOBJS += $(MOTIFOBJ:%=$(MOTIF_OBJPRE)%)
 TOOLKITSOURCE += $(MOTIFOBJ:%.o=motif/%.c)
--- a/ui/motif/range.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/range.c	Wed Dec 04 18:31:22 2024 +0100
@@ -34,104 +34,3 @@
 #include "../common/context.h"
 #include "../common/object.h"
 
-
-static UIWIDGET ui_scrollbar(UiObject *obj, UiOrientation orientation, UiRange *range, ui_callback f, void *userdata) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    int n = 0;
-    Arg args[16];
-    XtSetArg(args[n], XmNorientation, orientation == UI_HORIZONTAL ? XmHORIZONTAL : XmVERTICAL);
-    n++;
-    XtSetArg (args[n], XmNmaximum, 10);
-    n++;
-    XtSetArg (args[n], XmNsliderSize, 1);
-    n++;
-    Widget parent = ct->prepare(ct, args, &n, FALSE);
-    Widget scrollbar = XmCreateScrollBar(parent, "scrollbar", args, n);
-    XtManageChild(scrollbar);
-    ct->add(ct, scrollbar);
-    
-    if(range) {
-        range->get = ui_scrollbar_get;
-        range->set = ui_scrollbar_set;
-        range->setrange = ui_scrollbar_setrange;
-        range->setextent = ui_scrollbar_setextent;
-        range->obj = scrollbar;
-    }
-    
-    if(f) {
-        UiEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = userdata;
-        event->callback = f;
-        event->value = 0;
-        XtAddCallback(
-                scrollbar,
-                XmNvalueChangedCallback,
-                (XtCallbackProc)ui_scrollbar_callback,
-                event);
-    }
-    
-    return scrollbar;
-}
-
-UIWIDGET ui_hscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata) {
-    return ui_scrollbar(obj, UI_HORIZONTAL, range, f, userdata);
-}
-
-UIWIDGET ui_vscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata) {
-    return ui_scrollbar(obj, UI_VERTICAL, range, f, userdata);
-}
-
-void ui_scrollbar_callback(Widget scrollbar, UiEventData *event, XtPointer cdata) {
-    UiEvent e;
-    e.obj = event->obj;
-    e.window = event->obj->window;
-    e.document = event->obj->ctx->document;
-    e.intval = event->value;
-    event->callback(&e, event->userdata);
-}
-
-double ui_scrollbar_get(UiRange *range) {
-    int intval;
-    XtVaGetValues(
-            range->obj,
-            XmNvalue,
-            &intval,
-            NULL);
-    double value = (double)intval / 10;
-    range->value = value;
-    return value;
-}
-
-void   ui_scrollbar_set(UiRange *range, double value) {
-    XtVaSetValues(
-            range->obj,
-            XmNvalue,
-            (int)(value * 10),
-            NULL);
-    range->value = value;
-}
-
-void   ui_scrollbar_setrange(UiRange *range, double min, double max) {
-    XtVaSetValues(
-            range->obj,
-            XmNminimum,
-            (int)(min * 10),
-            XmNmaximum,
-            (int)(max * 10),
-            NULL);
-    range->min = min;
-    range->max = max;
-}
-
-void   ui_scrollbar_setextent(UiRange *range, double extent) {
-    XtVaSetValues(
-            range->obj,
-            XmNsliderSize,
-            (int)(extent * 10),
-            NULL);
-    range->extent = extent;
-}
--- a/ui/motif/range.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/range.h	Wed Dec 04 18:31:22 2024 +0100
@@ -36,11 +36,6 @@
 extern "C" {
 #endif
 
-void ui_scrollbar_callback(Widget scrollbar, UiEventData *event, XtPointer cdata);
-double ui_scrollbar_get(UiRange *range);
-void   ui_scrollbar_set(UiRange *range, double value);
-void   ui_scrollbar_setrange(UiRange *range, double min, double max);
-void   ui_scrollbar_setextent(UiRange *range, double extent);
 
 
 #ifdef __cplusplus
--- a/ui/motif/stock.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/stock.c	Wed Dec 04 18:31:22 2024 +0100
@@ -33,44 +33,4 @@
 #include "../ui/properties.h"
 #include <cx/hash_map.h>
 
-static CxMap *stock_items;
 
-void ui_stock_init() {
-    stock_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 64);
-    
-    ui_add_stock_item(UI_STOCK_NEW, "New", "Ctrl<Key>N", "Ctrl+N", NULL);
-    ui_add_stock_item(UI_STOCK_OPEN, "Open", "Ctrl<Key>O", "Ctrl+O", NULL);
-    ui_add_stock_item(UI_STOCK_SAVE, "Save", "Ctrl<Key>S", "Ctrl+S", NULL);
-    ui_add_stock_item(UI_STOCK_SAVE_AS, "Save as ...", NULL, NULL, NULL);
-    ui_add_stock_item(UI_STOCK_REVERT_TO_SAVED, "Revert to saved", NULL, NULL, NULL);
-    ui_add_stock_item(UI_STOCK_CLOSE, "Close", "Ctrl<Key>W", "Ctrl+W", NULL);
-    ui_add_stock_item(UI_STOCK_UNDO, "Undo", "Ctrl<Key>Z", "Ctrl+Z", NULL);
-    ui_add_stock_item(UI_STOCK_REDO, "Redo", NULL, NULL, NULL);
-    ui_add_stock_item(UI_STOCK_GO_BACK, "Back", NULL, NULL, NULL);
-    ui_add_stock_item(UI_STOCK_GO_FORWARD, "Forward", NULL, NULL, NULL);
-    ui_add_stock_item(UI_STOCK_CUT, "Cut", "Ctrl<Key>X", "Ctrl+X", NULL);
-    ui_add_stock_item(UI_STOCK_COPY, "Copy", "Ctrl<Key>C", "Ctrl+C", NULL);
-    ui_add_stock_item(UI_STOCK_PASTE, "Paste", "Ctrl<Key>V", "Ctrl+V", NULL);
-    ui_add_stock_item(UI_STOCK_DELETE, "Delete", NULL, NULL, NULL);
-}
-
-void ui_add_stock_item(char *id, char *label, char *accelerator, char *accelerator_label, void *icon) {
-    UiStockItem *i = malloc(sizeof(UiStockItem));
-    i->label = label;
-    i->accelerator = accelerator;
-    i->accelerator_label = accelerator_label;
-    // TODO: icon
-    
-    cxMapPut(stock_items, id, i);
-}
-
-UiStockItem* ui_get_stock_item(char *id) {
-    UiStockItem *item = cxMapGet(stock_items, id);
-    if(item) {
-        char *label = uistr_n(id);
-        if(label) {
-            item->label = label;
-        }
-    }
-    return item;
-}
--- a/ui/motif/stock.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/stock.h	Wed Dec 04 18:31:22 2024 +0100
@@ -35,18 +35,7 @@
 extern "C" {
 #endif
 
-typedef struct UiStockItem {
-    char *label;
-    char *accelerator;
-    char *accelerator_label;
-    // TODO: icon
-} UiStockItem;
-    
-void ui_stock_init();
 
-void ui_add_stock_item(char *id, char *label, char *accelerator, char *accelerator_label, void *icon);
-
-UiStockItem* ui_get_stock_item(char *id);
 
 #ifdef	__cplusplus
 }
--- a/ui/motif/text.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/text.c	Wed Dec 04 18:31:22 2024 +0100
@@ -32,476 +32,3 @@
 #include "text.h"
 #include "container.h"
 
-
-UIWIDGET ui_textarea_var(UiObject *obj, UiVar *var) {
-    UiContainer *ct = uic_get_current_container(obj);
-    int n = 0;
-    Arg args[16];
-    
-    //XtSetArg(args[n], XmNeditable, TRUE);
-    //n++;
-    XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT);
-    n++;
-    
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    Widget text_area = XmCreateScrolledText(parent, "text_area", args, n);
-    ct->add(ct, XtParent(text_area));
-    XtManageChild(text_area);
-    
-    UiTextArea *uitext = cxMalloc(
-            obj->ctx->allocator,
-            sizeof(UiTextArea));
-    uitext->ctx = obj->ctx;
-    uitext->last_selection_state = 0;
-    XtAddCallback(
-                text_area,
-                XmNmotionVerifyCallback,
-                (XtCallbackProc)ui_text_selection_callback,
-                uitext);
-    
-    // bind value
-    if(var->value) {
-        UiText *value = var->value;
-        if(value->value.ptr) {
-            XmTextSetString(text_area, value->value.ptr);
-            value->value.free(value->value.ptr);
-        }
-        
-        value->set = ui_textarea_set;
-        value->get = ui_textarea_get;
-        value->getsubstr = ui_textarea_getsubstr;
-        value->insert = ui_textarea_insert;
-        value->setposition = ui_textarea_setposition;
-        value->position = ui_textarea_position;
-        value->selection = ui_textarea_selection;
-        value->length = ui_textarea_length;
-        value->value.ptr = NULL;
-        value->obj = text_area;
-        
-        if(!value->undomgr) {
-            value->undomgr = ui_create_undomgr();
-        }
-        
-        XtAddCallback(
-                text_area,
-                XmNmodifyVerifyCallback,
-                (XtCallbackProc)ui_text_modify_callback,
-                var);
-    }
-    
-    return text_area;
-}
-
-UIWIDGET ui_textarea(UiObject *obj, UiText *value) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = value;
-    var->type = UI_VAR_SPECIAL;
-    return ui_textarea_var(obj, var);
-}
-
-UIWIDGET ui_textarea_nv(UiObject *obj, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_TEXT);
-    if(var) {
-        return ui_textarea_var(obj, var);
-    } else {
-        // TODO: error
-    }
-    return NULL;
-}
-
-char* ui_textarea_get(UiText *text) {
-    if(text->value.ptr) {
-        text->value.free(text->value.ptr);
-    }
-    char *str = XmTextGetString(text->obj);
-    text->value.ptr = str;
-    text->value.free = (ui_freefunc)XtFree;
-    return str;
-}
-
-void ui_textarea_set(UiText *text, const char *str) {
-    XmTextSetString(text->obj, str);
-    if(text->value.ptr) {
-        text->value.free(text->value.ptr);
-    }
-    text->value.ptr = NULL;
-}
-
-char* ui_textarea_getsubstr(UiText *text, int begin, int end) {
-    if(text->value.ptr) {
-        text->value.free(text->value.ptr);
-    }
-    int length = end - begin;
-    char *str = XtMalloc(length + 1);
-    XmTextGetSubstring(text->obj, begin, length, length + 1, str);
-    text->value.ptr = str;
-    text->value.free = (ui_freefunc)XtFree;
-    return str;
-}
-
-void ui_textarea_insert(UiText *text, int pos, char *str) {
-    text->value.ptr = NULL;
-    XmTextInsert(text->obj, pos, str);
-    if(text->value.ptr) {
-        text->value.free(text->value.ptr);
-    }
-}
-
-void ui_textarea_setposition(UiText *text, int pos) {
-    XmTextSetInsertionPosition(text->obj, pos);
-}
-
-int ui_textarea_position(UiText *text) {
-    long begin;
-    long end;
-    XmTextGetSelectionPosition(text->obj, &begin, &end);
-    text->pos = begin;
-    return text->pos;
-}
-
-void ui_textarea_selection(UiText *text, int *begin, int *end) {
-    XmTextGetSelectionPosition(text->obj, (long*)begin, (long*)end);
-}
-
-int ui_textarea_length(UiText *text) {
-    return (int)XmTextGetLastPosition(text->obj);
-}
-
-
-void ui_text_set(UiText *text, char *str) {
-    if(text->set) {
-        text->set(text, str);
-    } else {
-        if(text->value.ptr) {
-            text->value.free(text->value.ptr);
-        }
-        text->value.ptr = XtNewString(str);
-        text->value.free = (ui_freefunc)XtFree;
-    }
-}
-
-char* ui_text_get(UiText *text) {
-    if(text->get) {
-        return text->get(text);
-    } else {
-        return text->value.ptr;
-    }
-}
-
-
-UiUndoMgr* ui_create_undomgr() {
-    UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr));
-    mgr->begin = NULL;
-    mgr->end = NULL;
-    mgr->cur = NULL;
-    mgr->length = 0;
-    mgr->event = 1;
-    return mgr;
-}
-
-void ui_destroy_undomgr(UiUndoMgr *mgr) {
-    UiTextBufOp *op = mgr->begin;
-    while(op) {
-        UiTextBufOp *nextOp = op->next;
-        if(op->text) {
-            free(op->text);
-        }
-        free(op);
-        op = nextOp;
-    }
-    free(mgr);
-}
-
-void ui_text_selection_callback(
-        Widget widget,
-        UiTextArea *textarea,
-        XtPointer data)
-{
-    long left = 0;
-    long right = 0;
-    XmTextGetSelectionPosition(widget, &left, &right);
-    int sel = left < right ? 1 : 0;
-    if(sel != textarea->last_selection_state) {
-        if(sel) {
-            ui_set_group(textarea->ctx, UI_GROUP_SELECTION);
-        } else {
-            ui_unset_group(textarea->ctx, UI_GROUP_SELECTION);
-        }
-    }
-    textarea->last_selection_state = sel;
-}
-
-void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data) {
-    UiText *value = var->value;
-    if(!value->obj) {
-        // TODO: bug, fix
-        return;
-    }
-    if(!value->undomgr) {
-        value->undomgr = ui_create_undomgr();
-    }
-    
-    XmTextVerifyCallbackStruct *txv = (XmTextVerifyCallbackStruct*)data;
-    int type = txv->text->length > 0 ? UI_TEXTBUF_INSERT : UI_TEXTBUF_DELETE;
-    UiUndoMgr *mgr = value->undomgr;
-    if(!mgr->event) {
-        return;
-    }
-    
-    char *text = txv->text->ptr;
-    int length = txv->text->length;
-    
-    if(mgr->cur) {
-        UiTextBufOp *elm = mgr->cur->next;
-        if(elm) {
-            mgr->cur->next = NULL;
-            mgr->end = mgr->cur;
-            while(elm) {
-                elm->prev = NULL;   
-                UiTextBufOp *next = elm->next;
-                ui_free_textbuf_op(elm);
-                elm = next;
-            }
-        }
-        
-        UiTextBufOp *last_op = mgr->cur;
-        if(
-            last_op->type == UI_TEXTBUF_INSERT &&
-            ui_check_insertstr(last_op->text, last_op->len, text, length) == 0)
-        {
-            // append text to last op       
-            int ln = last_op->len;
-            char *newtext = malloc(ln + length + 1);
-            memcpy(newtext, last_op->text, ln);
-            memcpy(newtext+ln, text, length);
-            newtext[ln+length] = '\0';
-            
-            last_op->text = newtext;
-            last_op->len = ln + length;
-            last_op->end += length;
-            
-            return;
-        }
-    }
-    
-    char *str;
-    if(type == UI_TEXTBUF_INSERT) {
-        str = malloc(length + 1);
-        memcpy(str, text, length);
-        str[length] = 0;
-    } else {
-        length = txv->endPos - txv->startPos;
-        str = malloc(length + 1);
-        XmTextGetSubstring(value->obj, txv->startPos, length, length+1, str);
-    }
-    
-    UiTextBufOp *op = malloc(sizeof(UiTextBufOp));
-    op->prev = NULL;
-    op->next = NULL;
-    op->type = type;
-    op->start = txv->startPos;
-    op->end = txv->endPos + 1;
-    op->len = length;
-    op->text = str;
-    
-    cx_linked_list_add(
-            (void**)&mgr->begin,
-            (void**)&mgr->end,
-            offsetof(UiTextBufOp, prev),
-            offsetof(UiTextBufOp, next),
-            op);
-    
-    mgr->cur = op;
-}
-
-int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) {
-    // return 1 if oldstr + newstr are one word
-    
-    int has_space = 0;
-    for(int i=0;i<oldlen;i++) {
-        if(oldstr[i] < 33) {
-            has_space = 1;
-            break;
-        }
-    }
-    
-    for(int i=0;i<newlen;i++) {
-        if(has_space && newstr[i] > 32) {
-            return 1;
-        }
-    }
-    
-    return 0;
-}
-
-void ui_free_textbuf_op(UiTextBufOp *op) {
-    if(op->text) {
-        free(op->text);
-    }
-    free(op);
-}
-
-
-void ui_text_undo(UiText *value) {
-    UiUndoMgr *mgr = value->undomgr;
-    
-    if(mgr->cur) {
-        UiTextBufOp *op = mgr->cur;
-        mgr->event = 0;
-        switch(op->type) {
-            case UI_TEXTBUF_INSERT: {
-                XmTextReplace(value->obj, op->start, op->end, "");
-                break;
-            }
-            case UI_TEXTBUF_DELETE: {
-                XmTextInsert(value->obj, op->start, op->text);
-                break;
-            }
-        }
-        mgr->event = 1;
-        mgr->cur = mgr->cur->prev;
-    }
-}
-
-void ui_text_redo(UiText *value) {
-    UiUndoMgr *mgr = value->undomgr;
-    
-    UiTextBufOp *elm = NULL;
-    if(mgr->cur) {
-        if(mgr->cur->next) {
-            elm = mgr->cur->next;
-        }
-    } else if(mgr->begin) {
-        elm = mgr->begin;
-    }
-    
-    if(elm) {
-        UiTextBufOp *op = elm;
-        mgr->event = 0;
-        switch(op->type) {
-            case UI_TEXTBUF_INSERT: {
-                XmTextInsert(value->obj, op->start, op->text);
-                break;
-            }
-            case UI_TEXTBUF_DELETE: {
-                XmTextReplace(value->obj, op->start, op->end, "");
-                break;
-            }
-        }
-        mgr->event = 1;
-        mgr->cur = elm;
-    }
-}
-
-
-/* ------------------------- textfield ------------------------- */
-
-static UIWIDGET create_textfield(UiObject *obj, int width, UiBool frameless, UiBool password, UiString *value) {
-    UiContainer *ct = uic_get_current_container(obj);
-    int n = 0;
-    Arg args[16];
-    XtSetArg(args[n], XmNeditMode, XmSINGLE_LINE_EDIT);
-    n++;
-    if(width > 0) {
-        XtSetArg(args[n], XmNcolumns, width / 2 + 1);
-        n++;
-    }
-    if(frameless) {
-        XtSetArg(args[n], XmNshadowThickness, 0);
-        n++;
-    }
-    if(password) {
-        // TODO
-    }
-    
-    Widget parent = ct->prepare(ct, args, &n, FALSE);
-    Widget textfield = XmCreateText(parent, "text_field", args, n);
-    ct->add(ct, textfield);
-    XtManageChild(textfield);
-    
-    // bind value
-    if(value) {
-        if(value->value.ptr) {
-            XmTextSetString(textfield, value->value.ptr);
-            value->value.free(value->value.ptr);
-        }
-        
-        value->set = ui_textfield_set;
-        value->get = ui_textfield_get;
-        value->value.ptr = NULL;
-        value->obj = textfield;
-    }
-    
-    return textfield;
-}
-
-static UIWIDGET create_textfield_nv(UiObject *obj, int width, UiBool frameless, UiBool password, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_STRING);
-    if(var) {
-        UiString *value = var->value;
-        return ui_textfield(obj, value);
-    } else {
-        // TODO: error
-    }
-    return NULL;
-}
-
-UIWIDGET ui_textfield(UiObject *obj, UiString *value) {
-    return create_textfield(obj, 0, FALSE, FALSE, value);
-}
-
-UIWIDGET ui_textfield_nv(UiObject *obj, char *varname) {
-    return create_textfield_nv(obj, 0, FALSE, FALSE, varname);
-}
-
-UIWIDGET ui_textfield_w(UiObject *obj, int width, UiString *value) {
-    return create_textfield(obj, width, FALSE, FALSE, value);
-}
-
-UIWIDGET ui_textfield_wnv(UiObject *obj, int width, char *varname) {
-    return create_textfield_nv(obj, width, FALSE, FALSE, varname);
-}
-
-UIWIDGET ui_frameless_textfield(UiObject *obj, UiString *value) {
-    return create_textfield(obj, 0, TRUE, FALSE, value);
-}
-
-UIWIDGET ui_frameless_textfield_nv(UiObject *obj, char *varname) {
-    return create_textfield_nv(obj, 0, TRUE, FALSE, varname);
-}
-
-UIWIDGET ui_passwordfield(UiObject *obj, UiString *value) {
-    return create_textfield(obj, 0, FALSE, TRUE, value);
-}
-
-UIWIDGET ui_passwordfield_nv(UiObject *obj, char *varname) {
-    return create_textfield_nv(obj, 0, FALSE, TRUE, varname);
-}
-
-UIWIDGET ui_passwordfield_w(UiObject *obj, int width, UiString *value) {
-    return create_textfield(obj, width, FALSE, TRUE, value);
-}
-
-UIWIDGET ui_passwordfield_wnv(UiObject *obj, int width, char *varname) {
-    return create_textfield_nv(obj, width, FALSE, TRUE, varname);
-}
-
-
-char* ui_textfield_get(UiString *str) {
-    if(str->value.ptr) {
-        str->value.free(str->value.ptr);
-    }
-    char *value = XmTextGetString(str->obj);
-    str->value.ptr = value;
-    str->value.free = (ui_freefunc)XtFree;
-    return value;
-}
-
-void ui_textfield_set(UiString *str, char *value) {
-    XmTextSetString(str->obj, value);
-    if(str->value.ptr) {
-        str->value.free(str->value.ptr);
-    }
-    str->value.ptr = NULL;
-}
-
--- a/ui/motif/text.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/text.h	Wed Dec 04 18:31:22 2024 +0100
@@ -37,54 +37,6 @@
 extern "C" {
 #endif
 
-#define UI_TEXTBUF_INSERT 0
-#define UI_TEXTBUF_DELETE 1
-typedef struct UiTextBufOp UiTextBufOp;
-struct UiTextBufOp {
-    UiTextBufOp *prev;
-    UiTextBufOp *next;
-    int  type; // UI_TEXTBUF_INSERT, UI_TEXTBUF_DELETE
-    int  start;
-    int  end;
-    int  len;
-    char *text;
-};
-    
-typedef struct UiUndoMgr {
-    UiTextBufOp *begin;
-    UiTextBufOp *end;
-    UiTextBufOp *cur;
-    int         length;
-    int         event;
-} UiUndoMgr;
-
-typedef struct UiTextArea {
-    UiContext *ctx;
-    int last_selection_state;
-} UiTextArea;
-    
-char* ui_textarea_get(UiText *text);
-void ui_textarea_set(UiText *text, const char *str);
-char* ui_textarea_getsubstr(UiText *text, int begin, int end);
-void ui_textarea_insert(UiText *text, int pos, char *str);
-void ui_textarea_setposition(UiText *text, int pos);
-int ui_textarea_position(UiText *text);
-void ui_textarea_selection(UiText *text, int *begin, int *end);
-int ui_textarea_length(UiText *text);
-
-UiUndoMgr* ui_create_undomgr();
-void ui_destroy_undomgr(UiUndoMgr *mgr);
-void ui_text_selection_callback(
-        Widget widget,
-        UiTextArea *textarea,
-        XtPointer data);
-void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data);
-int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen);
-void ui_free_textbuf_op(UiTextBufOp *op);
-
-char* ui_textfield_get(UiString *str);
-void ui_textfield_set(UiString *str, char *value);
-
 #ifdef	__cplusplus
 }
 #endif
--- a/ui/motif/toolbar.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/toolbar.c	Wed Dec 04 18:31:22 2024 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2014 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -42,340 +42,3 @@
 
 #include "../common/context.h"
 
-static CxMap *toolbar_items;
-static CxList *defaults;
-
-void ui_toolbar_init() {
-    toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
-    defaults = cxLinkedListCreateSimple(CX_STORE_POINTERS);
-}
-
-void ui_toolitem(char *name, char *label, ui_callback f, void *userdata) {
-    UiToolItem *item = malloc(sizeof(UiToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_widget;
-    item->label = label;
-    item->image = NULL;
-    item->callback = f;
-    item->userdata = userdata;
-    item->groups = NULL;
-    item->isimportant = FALSE;
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-void ui_toolitem_st(char *name, char *stockid, ui_callback f, void *userdata) {
-    ui_toolitem_stgr(name, stockid, f, userdata, -1);
-}
-
-void ui_toolitem_stgr(char *name, char *stockid, ui_callback f, void *userdata, ...) {
-    UiStToolItem *item = malloc(sizeof(UiStToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_st_widget;
-    item->stockid = stockid;
-    item->callback = f;
-    item->userdata = userdata;
-    item->groups = NULL;
-    item->isimportant = FALSE;
-    
-    // add groups
-    va_list ap;
-    va_start(ap, userdata);
-    int group;
-    while((group = va_arg(ap, int)) != -1) {
-        if(!item->groups) {
-            item->groups = cxArrayListCreateSimple(sizeof(int), 16);
-        }
-        cxListAdd(item->groups, &group);
-    }
-    va_end(ap);
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-void ui_toolitem_img(char *name, char *label, char *img, ui_callback f, void *udata) {
-    // TODO
-    
-    UiToolItem *item = malloc(sizeof(UiToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_widget;
-    item->label = label;
-    item->image = img;
-    item->callback = f;
-    item->userdata = udata;
-    item->groups = NULL;
-    item->isimportant = FALSE;
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-
-void ui_toolitem_toggle_stgr(char *name, char *stockid, ui_callback f, void *udata, ...) {
-    // TODO
-    
-    UiStToolItem *item = malloc(sizeof(UiStToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_st_toggle_widget;
-    item->stockid = stockid;
-    item->callback = f;
-    item->userdata = udata;
-    item->groups = NULL;
-    item->isimportant = FALSE;
-    
-    // add groups
-    va_list ap;
-    va_start(ap, udata);
-    int group;
-    while((group = va_arg(ap, int)) != -1) {
-        if(!item->groups) {
-            item->groups = cxArrayListCreateSimple(sizeof(int), 16);
-        }
-        cxListAdd(item->groups, &group);
-    }
-    va_end(ap);
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-void ui_toolitem_toggle_imggr(char *name, char *label, char *img, ui_callback f, void *udata, ...) {
-    // TODO
-    
-    UiToolItem *item = malloc(sizeof(UiToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget;
-    item->label = label;
-    item->image = img;
-    item->callback = f;
-    item->userdata = udata;
-    item->groups = NULL;
-    item->isimportant = FALSE;
-    
-    // add groups
-    va_list ap;
-    va_start(ap, udata);
-    int group;
-    while((group = va_arg(ap, int)) != -1) {
-        if(!item->groups) {
-            item->groups = cxArrayListCreateSimple(sizeof(int), 16);
-        }
-        cxListAdd(item->groups, &group);
-    }
-    va_end(ap);
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-void ui_toolbar_combobox(
-        char *name,
-        UiList *list,
-        ui_getvaluefunc getvalue,
-        ui_callback f,
-        void *udata)
-{
-    UiToolbarComboBox *cb = malloc(sizeof(UiToolbarComboBox));
-    cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox;  
-    cb->list = list;
-    cb->getvalue = getvalue;
-    cb->callback = f;
-    cb->userdata = udata;
-    
-    cxMapPut(toolbar_items, name, cb);
-}
-
-void ui_toolbar_combobox_str(
-        char *name,
-        UiList *list,
-        ui_callback f,
-        void *udata)
-{
-    ui_toolbar_combobox(name, list, ui_strmodel_getvalue, f, udata);
-}
-
-void ui_toolbar_combobox_nv(
-        char *name,
-        char *listname,
-        ui_getvaluefunc getvalue,
-        ui_callback f,
-        void *udata)
-{
-    UiToolbarComboBoxNV *cb = malloc(sizeof(UiToolbarComboBoxNV));
-    cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox_nv;  
-    cb->listname = listname;
-    cb->getvalue = getvalue;
-    cb->callback = f;
-    cb->userdata = udata;
-    
-    cxMapPut(toolbar_items, name, cb);
-}
-
-
-void ui_toolbar_add_default(char *name) {
-    char *s = strdup(name);
-    cxListAdd(defaults, s);
-}
-
-Widget ui_create_toolbar(UiObject *obj, Widget parent) {
-    if(!defaults) {
-        return NULL;
-    }
-    
-    Arg args[8];
-    XtSetArg(args[0], XmNshadowType, XmSHADOW_ETCHED_OUT);
-    XtSetArg(args[1], XmNshadowThickness, 1);
-    XtSetArg(args[2], XmNtopAttachment, XmATTACH_FORM);
-    XtSetArg(args[3], XmNleftAttachment, XmATTACH_FORM);
-    XtSetArg(args[4], XmNrightAttachment, XmATTACH_FORM);
-    Widget frame = XmCreateFrame(parent, "toolbar_frame", args, 5);
-    
-    XtSetArg(args[0], XmNorientation, XmHORIZONTAL);
-    XtSetArg(args[1], XmNpacking, XmPACK_TIGHT);
-    XtSetArg(args[2], XmNspacing, 1);
-    Widget toolbar = XmCreateRowColumn (frame, "toolbar", args, 3);
-    
-    CxIterator i = cxListIterator(defaults);
-    cx_foreach(char *, def, i) {
-        UiToolItemI *item = cxMapGet(toolbar_items, def);
-        if(item) {
-            item->add_to(toolbar, item, obj);
-        } else if(!strcmp(def, "@separator")) {
-            // TODO
-        } else {
-            fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", def);
-        }
-    }
-    
-    XtManageChild(toolbar);
-    XtManageChild(frame);
-    
-    return frame;
-}
-
-void add_toolitem_widget(Widget parent, UiToolItem *item, UiObject *obj) {
-    Arg args[4];
-    
-    XmString label = XmStringCreateLocalized(item->label);
-    XtSetArg(args[0], XmNlabelString, label);
-    XtSetArg(args[1], XmNshadowThickness, 1);
-    XtSetArg(args[2], XmNtraversalOn, FALSE);
-    Widget button = XmCreatePushButton(parent, "toolbar_button", args, 3);
-    
-    XmStringFree(label);
-    
-    if(item->callback) {
-        UiEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = item->userdata;
-        event->callback = item->callback;
-        XtAddCallback(
-                button,
-                XmNactivateCallback,
-                (XtCallbackProc)ui_push_button_callback,
-                event);
-    }
-    
-    XtManageChild(button);
-    
-    if(item->groups) {
-        uic_add_group_widget(obj->ctx, button, (ui_enablefunc)XtSetSensitive, item->groups);
-    }
-}
-
-void add_toolitem_st_widget(Widget parent, UiStToolItem *item, UiObject *obj) {
-    Arg args[8];
-    
-    UiStockItem *stock_item = ui_get_stock_item(item->stockid);
-     
-    XmString label = XmStringCreateLocalized(stock_item->label);
-    XtSetArg(args[0], XmNlabelString, label);
-    XtSetArg(args[1], XmNshadowThickness, 1);
-    XtSetArg(args[2], XmNtraversalOn, FALSE);
-    Widget button = XmCreatePushButton(parent, "toolbar_button", args, 3);
- 
-    XmStringFree(label);
-    
-    if(item->callback) {
-        UiEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = item->userdata;
-        event->callback = item->callback;
-        XtAddCallback(
-                button,
-                XmNactivateCallback,
-                (XtCallbackProc)ui_push_button_callback,
-                event);
-    }
-    
-    XtManageChild(button);
-    
-    if(item->groups) {
-        uic_add_group_widget(obj->ctx, button, (ui_enablefunc)XtSetSensitive, item->groups);
-    }
-}
-
-void add_toolitem_toggle_widget(Widget parent, UiToolItem *item, UiObject *obj) {
-    Arg args[8];
-    
-    XmString label = XmStringCreateLocalized(item->label);
-    XtSetArg(args[0], XmNlabelString, label);
-    XtSetArg(args[1], XmNshadowThickness, 1);
-    XtSetArg(args[2], XmNtraversalOn, FALSE);
-    XtSetArg(args[3], XmNindicatorOn, XmINDICATOR_NONE);
-    Widget button = XmCreateToggleButton(parent, "toolbar_toggle_button", args, 4);
-    
-    XmStringFree(label);
-    
-    if(item->callback) {
-        UiEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = item->userdata;
-        event->callback = item->callback;
-        XtAddCallback(
-                button,
-                XmNvalueChangedCallback,
-                (XtCallbackProc)ui_toggle_button_callback,
-                event);
-    }
-    
-    XtManageChild(button);
-    
-    if(item->groups) {
-        uic_add_group_widget(obj->ctx, button, (ui_enablefunc)XtSetSensitive, item->groups);
-    }
-}
-
-void add_toolitem_st_toggle_widget(Widget parent, UiStToolItem *item, UiObject *obj) {
-    
-}
-
-void add_toolbar_combobox(Widget tb, UiToolbarComboBox *item, UiObject *obj) {
-    UiListView *listview = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiListView));
-    
-    UiVar *var = cxMalloc(obj->ctx->allocator, sizeof(UiVar));
-    var->value = item->list;
-    var->type = UI_VAR_SPECIAL;
-    
-    Arg args[8];
-    XtSetArg(args[0], XmNshadowThickness, 1);
-    XtSetArg(args[1], XmNindicatorOn, XmINDICATOR_NONE);
-    XtSetArg(args[2], XmNtraversalOn, FALSE);
-    XtSetArg(args[3], XmNwidth, 120);
-    Widget combobox = XmCreateDropDownList(tb, "toolbar_combobox", args, 4);
-    XtManageChild(combobox);
-    listview->widget = combobox;
-    listview->list = var;
-    listview->getvalue = item->getvalue;
-    
-    ui_listview_update(NULL, listview);
-    
-    if(item->callback) {
-        // TODO:
-        
-    }
-}
-
-void add_toolbar_combobox_nv(Widget tb, UiToolbarComboBoxNV *item, UiObject *obj) {
-    
-}
--- a/ui/motif/toolbar.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/toolbar.h	Wed Dec 04 18:31:22 2024 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2014 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -37,66 +37,6 @@
 extern "C" {
 #endif
 
-typedef struct UiToolItemI    UiToolItemI;
-typedef struct UiToolItem     UiToolItem;
-typedef struct UiStToolItem   UiStToolItem;
-
-typedef struct UiToolbarComboBox   UiToolbarComboBox;
-typedef struct UiToolbarComboBoxNV UiToolbarComboBoxNV;
-
-typedef void(*ui_toolbar_add_f)(Widget, UiToolItemI*, UiObject*);
-
-struct UiToolItemI {
-    ui_toolbar_add_f  add_to;
-};
-
-struct UiToolItem {
-    UiToolItemI item;
-    char           *label;
-    void           *image;
-    ui_callback    callback;
-    void           *userdata;
-    CxList         *groups;
-    Boolean        isimportant;
-};
-
-struct UiStToolItem {
-    UiToolItemI    item;
-    char           *stockid;
-    ui_callback    callback;
-    void           *userdata;
-    CxList         *groups;
-    Boolean        isimportant;
-};
-
-struct UiToolbarComboBox {
-    UiToolItemI         item;
-    UiList              *list;
-    ui_getvaluefunc     getvalue;
-    ui_callback         callback;
-    void                *userdata;
-};
-
-struct UiToolbarComboBoxNV {
-    UiToolItemI         item;
-    char                *listname;
-    ui_getvaluefunc     getvalue;
-    ui_callback         callback;
-    void                *userdata;
-};
-
-void ui_toolbar_init();
-
-Widget ui_create_toolbar(UiObject *obj, Widget parent);
-
-void add_toolitem_widget(Widget tb, UiToolItem *item, UiObject *obj);
-void add_toolitem_st_widget(Widget tb, UiStToolItem *item, UiObject *obj);
-void add_toolitem_toggle_widget(Widget tb, UiToolItem *item, UiObject *obj);
-void add_toolitem_st_toggle_widget(Widget tb, UiStToolItem *item, UiObject *obj);
-
-void add_toolbar_combobox(Widget tb, UiToolbarComboBox *item, UiObject *obj);
-void add_toolbar_combobox_nv(Widget tb, UiToolbarComboBoxNV *item, UiObject *obj);
-
 #ifdef	__cplusplus
 }
 #endif
--- a/ui/motif/toolkit.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/toolkit.c	Wed Dec 04 18:31:22 2024 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2014 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -33,15 +33,21 @@
 
 #include "toolkit.h"
 #include "toolbar.h"
+#include "container.h"
 #include "stock.h"
+#include "../common/menu.h"
+#include "../common/toolbar.h"
 #include "../common/document.h"
 #include "../common/properties.h"
 #include <cx/buffer.h>
 
+#include <X11/Intrinsic.h>
+#include <Xm/CutPaste.h>
+
 static XtAppContext app;
 static Display *display;
 static Widget active_window;
-static char *application_name;
+static const char *application_name;
 
 static ui_callback   startup_func;
 static void          *startup_data;
@@ -68,6 +74,11 @@
         "*rt*fontType: FONT_IS_XFT",
         "*rt*fontName: Sans",
         "*rt*fontSize: 11",
+        
+        "*window_frame.shadowType: SHADOW_ETCHED_OUT",
+        "*window_frame.shadowThickness: 1",
+        "*togglebutton.shadowThickness: 1",
+        "*togglebutton.highlightThickness: 2",
 	NULL
 };
 
@@ -76,8 +87,9 @@
     read(event_pipe[0], &ptr, sizeof(void*));
 }
 
-void ui_init(char *appname, int argc, char **argv) { 
+void ui_init(const char *appname, int argc, char **argv) { 
     application_name = appname;
+    uic_init_global_context();
     
     XtToolkitInitialize();
     XtSetLanguageProc(NULL, NULL, NULL);
@@ -85,15 +97,10 @@
     XtAppSetFallbackResources(app, fallback);
     
     display =  XtOpenDisplay(app, NULL, appname, appname, NULL, 0, &argc, argv);
-    char **missing = NULL;
-    int nm = 0;
-    char *def = NULL;
-    XCreateFontSet(display, "-dt-interface system-medium-r-normal-s*utf*", &missing, &nm, &def);
     
     uic_docmgr_init();
-    ui_toolbar_init();
-    ui_stock_init();
-    
+    uic_menu_init();
+    uic_toolbar_init();
     uic_load_app_properties();
     
     if(pipe(event_pipe)) {
@@ -108,11 +115,11 @@
             NULL);
 }
 
-char* ui_appname() {
+const char* ui_appname() {
     return application_name;
 }
 
-Display* ui_get_display() {
+Display* ui_motif_get_display() {
     return display;
 }
 
@@ -157,11 +164,12 @@
 void ui_show(UiObject *obj) {
     uic_check_group_widgets(obj->ctx);
     XtRealizeWidget(obj->widget);
-    ui_window_dark_theme(XtDisplay(obj->widget), XtWindow(obj->widget)); // TODO: if
 }
 
-// implemented in window.c
-//void ui_close(UiObject *obj)
+void ui_close(UiObject *obj) {
+    
+}
+
 
 void ui_set_enabled(UIWIDGET widget, int enabled) {
     XtSetSensitive(widget, enabled);
@@ -182,17 +190,16 @@
 }
 
 static Boolean ui_job_finished(void *data) {
-    printf("WorkProc\n");
     UiJob *job = data;
-    
-    UiEvent event;
-    event.obj = job->obj;
-    event.window = job->obj->window;
-    event.document = job->obj->ctx->document;
-    event.intval = 0;
-    event.eventdata = NULL;
-
-    job->finish_callback(&event, job->finish_data);
+    if(job->finish_callback) {
+        UiEvent event;
+        event.obj = job->obj;
+        event.window = job->obj->window;
+        event.document = job->obj->ctx->document;
+        event.intval = 0;
+        event.eventdata = NULL;
+        job->finish_callback(&event, job->finish_data);
+    }
     free(job);
     return TRUE;
 }
@@ -201,7 +208,6 @@
     UiJob *job = data;
     int result = job->job_func(job->job_data);
     if(!result) {
-        printf("XtAppAddWorkProc\n");
         write(event_pipe[1], &job, sizeof(void*)); // hack
         XtAppAddWorkProc(app, ui_job_finished, job);
         
@@ -221,7 +227,6 @@
 }
 
 void ui_clipboard_set(char *str) {
-    printf("copy: {%s}\n", str);
     int length = strlen(str) + 1;
     
     Display *dp = XtDisplayOfObject(active_window);
@@ -287,6 +292,9 @@
     return active_window;
 }
 
+/*
+ * doesn't work with gnome anymore
+ */
 void ui_window_dark_theme(Display *dp, Window window) {
     Atom atom = XInternAtom(dp, "_GTK_THEME_VARIANT", False);
     Atom type = XInternAtom(dp, "UTF8_STRING", False);
@@ -300,3 +308,23 @@
             (const unsigned char*)"dark",
             4);
 }
+
+void ui_destroy_eventdata(Widget w, XtPointer *data, XtPointer d) {
+    free(data);
+}
+
+void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) {
+    if(!groups) {
+        return;
+    }
+    size_t ngroups = uic_group_array_size(groups);
+    ui_set_widget_ngroups(ctx, widget, groups, ngroups);
+}
+
+void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups) {
+    if(ngroups > 0) {
+        uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups);
+        ui_set_enabled(widget, FALSE);
+    }
+}
+
--- a/ui/motif/toolkit.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/toolkit.h	Wed Dec 04 18:31:22 2024 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2014 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -38,8 +38,6 @@
 extern "C" {
 #endif
 
-Display* ui_get_display();
-
 typedef struct UiEventData {
     UiObject    *obj;
     ui_callback callback;
@@ -47,6 +45,31 @@
     int         value;
 } UiEventData;
 
+typedef struct UiEventDataExt {
+    UiObject    *obj;
+    ui_callback callback;
+    void        *userdata;
+    ui_callback callback2;
+    void        *userdata2;
+    int         value0;
+    int         value1;
+    int         value2;
+    int         value3;
+    void        *customdata0;
+    void        *customdata1;
+    void        *customdata2;
+    void        *customdata3;
+} UiEventDataExt;
+
+typedef struct UiVarEventData {
+    UiObject    *obj;
+    UiVar       *var;
+    UiObserver  **observers;
+    ui_callback callback;
+    void        *userdata;
+    int         value;
+} UiVarEventData;
+
 typedef struct UiJob {
     UiObject      *obj;
     ui_threadfunc job_func;
@@ -60,12 +83,19 @@
 
 void ui_exit_mainloop();
 
+Display* ui_motif_get_display(void);
+
 void ui_set_active_window(Widget w);
 Widget ui_get_active_window();
 
 void ui_secondary_event_loop(int *loop);
 void ui_window_dark_theme(Display *dp, Window window);
 
+void ui_destroy_eventdata(Widget w, XtPointer *data, XtPointer d);
+
+void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) ;
+void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/ui/motif/tree.c	Wed Dec 04 08:57:35 2024 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,328 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2014 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.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "tree.h"
-
-#include "container.h"
-#include "../common/object.h"
-#include "../common/context.h"
-#include <cx/utils.h>
-#include <cx/compare.h>
-#include <cx/printf.h>
-
-UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb) {
-    // TODO: check if modelinfo is complete
-    
-    Arg args[32];
-    int n = 0;
-    
-    // create scrolled window
-    UiContainer *ct = uic_get_current_container(obj);
-    Widget parent = ct->prepare(ct, args, &n, TRUE);
-    
-    XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC);
-    n++;
-    XtSetArg(args[n], XmNshadowThickness, 0);
-    n++;
-    Widget scrollw = XmCreateScrolledWindow(parent, "scroll_win", args, n);
-    ct->add(ct, scrollw);
-    XtManageChild(scrollw);
-    
-    // create table headers
-    XmStringTable header = (XmStringTable)XtMalloc(
-            model->columns * sizeof(XmString));
-    for(int i=0;i<model->columns;i++) {
-        header[i] = XmStringCreateLocalized(model->titles[i]);
-    }
-    n = 0;
-    XtSetArg(args[n], XmNdetailColumnHeading, header);
-    n++;
-    XtSetArg(args[n], XmNdetailColumnHeadingCount, model->columns);
-    n++;
-    
-    // set res
-    XtSetArg(args[n], XmNlayoutType, XmDETAIL);
-    n++;
-    XtSetArg(args[n], XmNentryViewType, XmSMALL_ICON);
-    n++;
-    XtSetArg(args[n], XmNselectionPolicy, XmSINGLE_SELECT);
-    n++;
-    XtSetArg(args[n], XmNwidth, 600);
-    n++;
-    
-    // create widget
-    //UiContainer *ct = uic_get_current_container(obj);
-    //Widget parent = ct->add(ct, args, &n);
-    
-    Widget container = XmCreateContainer(scrollw, "table", args, n);
-    XtManageChild(container);
-    
-    // add callbacks
-    UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData));
-    event->obj = obj;
-    event->activate = cb.activate;
-    event->selection = cb.selection;
-    event->userdata = cb.userdata;
-    event->last_selection = NULL;
-    if(cb.selection) {
-        XtAddCallback(
-                container,
-                XmNselectionCallback,
-                (XtCallbackProc)ui_table_select_callback,
-                event);
-    }
-    if(cb.activate) {
-        XtAddCallback(
-                container,
-                XmNdefaultActionCallback,
-                (XtCallbackProc)ui_table_action_callback,
-                event);
-    }
-    
-    // add initial data
-    UiList *list = var->value;
-    void *data = list->first(list);
-    int width = 0;
-    while(data) {
-        int w = ui_add_icon_gadget(container, model, data);
-        if(w > width) {
-            width = w;
-        }
-        data = list->next(list);
-    }
-    
-    UiTableView *tableview = cxMalloc(obj->ctx->allocator, sizeof(UiTableView));
-    tableview->widget = container;
-    tableview->var = var;
-    tableview->model = model;
-    
-    // set new XmContainer width
-    XtVaSetValues(container, XmNwidth, width, NULL);
-    
-    // cleanup
-    for(int i=0;i<model->columns;i++) {
-        XmStringFree(header[i]);
-    }
-    XtFree((char*)header);
-    
-    return scrollw;
-}
-
-UIWIDGET ui_table(UiObject *obj, UiList *data, UiModel *model, UiListCallbacks cb) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = data;
-    var->type = UI_VAR_SPECIAL;
-    return ui_table_var(obj, var, model, cb);
-}
-
-void ui_table_update(UiEvent *event, UiTableView *view) {
-    // clear container
-    Widget *children;
-    int nc;
-
-    XtVaGetValues(
-            view->widget,
-            XmNchildren,
-            &children,
-            XmNnumChildren,
-            &nc,
-            NULL);
-
-    for(int i=0;i<nc;i++) {
-        XtDestroyWidget(children[i]);
-    }
-    
-    UiList *list = view->var->value;
-    
-    void *data = list->first(list);
-    int width = 0;
-    while(data) {
-        int w = ui_add_icon_gadget(view->widget, view->model, data);
-        if(w > width) {
-            width = w;
-        }
-        data = list->next(list);
-    }
-    
-}
-
-#define UI_COL_CHAR_WIDTH 12
-
-int ui_add_icon_gadget(Widget container, UiModel *model, void *data) {
-    int width = 50;
-    
-    if(model->columns == 0) {
-        return width;
-    }
-    
-    XmString label = NULL;
-    Arg args[8];
-    Boolean f;
-    // first column
-    if(model->types[0] != 12345678) { // TODO: icon/label type
-        char *str = ui_type_to_string(
-                model->types[0],
-                model->getvalue(data, 0),
-                &f);
-        
-        // column width
-        width += strlen(str) * UI_COL_CHAR_WIDTH;
-        
-        
-        XmString label = XmStringCreateLocalized(str);
-        XtSetArg(args[0], XmNlabelString, label);
-        if(f) {
-            free(str);
-        }
-    } else {
-        // TODO
-    }
-            
-    // remaining columns are the icon gadget details
-    XmStringTable details = (XmStringTable)XtMalloc(
-            (model->columns - 1) * sizeof(XmString));
-    for(int i=1;i<model->columns;i++) {
-        char *str = ui_type_to_string(
-                model->types[i],
-                model->getvalue(data, i),
-                &f);
-        
-        // column width
-        width += strlen(str) * UI_COL_CHAR_WIDTH;
-        
-        details[i - 1] = XmStringCreateLocalized(str);
-        if(f) {
-            free(str);
-        }
-    }
-    XtSetArg(args[1], XmNdetail, details);
-    XtSetArg(args[2], XmNdetailCount, model->columns - 1);
-    XtSetArg(args[3], XmNshadowThickness, 0); 
-    // create widget
-    Widget item = XmCreateIconGadget(container, "table_item", args, 4);
-    XtManageChild(item);
-    
-    // cleanup
-    XmStringFree(label);
-    for(int i=0;i<model->columns-1;i++) {
-        XmStringFree(details[i]);
-    }
-    XtFree((char*)details);
-    
-    return width;
-}
-
-char* ui_type_to_string(UiModelType type, void *data, Boolean *free) {
-    switch(type) {
-        case UI_STRING: *free = FALSE; return data;
-        case UI_INTEGER: {
-            *free = TRUE;
-            int *val = data;
-            cxmutstr str = cx_asprintf("%d", *val);
-            return str.ptr;
-        }
-        case UI_ICON: break; // TODO
-        case UI_ICON_TEXT: break; // TODO
-    }
-    *free = FALSE;
-    return NULL;
-}
-
-void ui_table_action_callback(
-        Widget widget,
-        UiTreeEventData *event,
-        XmContainerSelectCallbackStruct *sel)
-{ 
-    UiListSelection *selection = ui_list_selection(sel);
-    
-    UiEvent e;
-    e.obj = event->obj;
-    e.window = event->obj->window;
-    e.document = event->obj->ctx->document;
-    e.eventdata = selection;
-    e.intval = selection->count > 0 ? selection->rows[0] : -1;
-    event->activate(&e, event->userdata);
-    
-    free(event->last_selection->rows);
-    free(event->last_selection);
-    event->last_selection = selection;
-}
-
-void ui_table_select_callback(
-        Widget widget,
-        UiTreeEventData *event,
-        XmContainerSelectCallbackStruct *sel)
-{ 
-    UiListSelection *selection = ui_list_selection(sel);
-    if(!ui_compare_list_selection(selection, event->last_selection)) {
-        UiEvent e;
-        e.obj = event->obj;
-        e.window = event->obj->window;
-        e.document = event->obj->ctx->document;
-        e.eventdata = selection;
-        e.intval = selection->count > 0 ? selection->rows[0] : -1;
-        event->selection(&e, event->userdata);
-    }
-    if(event->last_selection) {
-        free(event->last_selection->rows);
-        free(event->last_selection);
-    }
-    event->last_selection = selection;
-}
-
-UiListSelection* ui_list_selection(XmContainerSelectCallbackStruct *xs) {
-    UiListSelection *selection = malloc(sizeof(UiListSelection));
-    selection->count = xs->selected_item_count;
-    selection->rows = calloc(selection->count, sizeof(int));
-    for(int i=0;i<selection->count;i++) {
-        int index;
-        XtVaGetValues(xs->selected_items[i], XmNpositionIndex, &index, NULL);
-        selection->rows[i] = index;
-    }
-    return selection;
-}
-
-Boolean ui_compare_list_selection(UiListSelection *s1, UiListSelection *s2) {
-    if(!s1 || !s2) {
-        return FALSE;
-    } 
-    if(s1->count != s2->count) {
-        return FALSE;
-    }
-    for(int i=0;i<s1->count;i++) {
-        if(s1->rows[i] != s2->rows[i]) {
-            return FALSE;
-        }
-    }
-    return TRUE;
-}
--- a/ui/motif/tree.h	Wed Dec 04 08:57:35 2024 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2014 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.
- */
-
-#ifndef TREE_H
-#define	TREE_H
-
-#include "../ui/tree.h"
-#include "../common/context.h"
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-typedef struct UiTreeEventData {
-    UiObject        *obj;
-    ui_callback     activate;
-    ui_callback     selection;
-    void            *userdata;
-    UiListSelection *last_selection;
-} UiTreeEventData;    
-
-typedef struct UiTableView {
-    Widget      widget;
-    UiVar       *var;
-    UiModel     *model;
-} UiTableView;
-
-void ui_table_update(UiEvent *event, UiTableView *view);
-int ui_add_icon_gadget(Widget container, UiModel *model, void *data);
-char* ui_type_to_string(UiModelType type, void *data, Boolean *free);
-
-void ui_table_action_callback(
-        Widget widget,
-        UiTreeEventData *event,
-        XmContainerSelectCallbackStruct *sel);
-void ui_table_select_callback(
-        Widget widget,
-        UiTreeEventData *event,
-        XmContainerSelectCallbackStruct *sel);
-
-UiListSelection* ui_list_selection(XmContainerSelectCallbackStruct *xs);
-
-Boolean ui_compare_list_selection(UiListSelection *s1, UiListSelection *s2);
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* TREE_H */
-
--- a/ui/motif/window.c	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/motif/window.c	Wed Dec 04 18:31:22 2024 +0100
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2014 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -36,6 +36,8 @@
 #include "../ui/window.h"
 #include "../common/context.h"
 
+#include "Grid.h"
+
 #include <cx/mempool.h>
 
 static int nwindows = 0;
@@ -45,17 +47,7 @@
 
 static void window_close_handler(Widget window, void *udata, void *cdata) {
     UiObject *obj = udata;
-    UiEvent ev;
-    ev.window = obj->window;
-    ev.document = obj->ctx->document;
-    ev.obj = obj;
-    ev.eventdata = NULL;
-    ev.intval = 0;
-    
-    if(obj->ctx->close_callback) {
-        obj->ctx->close_callback(&ev, obj->ctx->close_data);
-    }
-    // TODO: free UiObject
+    uic_object_destroy(obj);
     
     nwindows--;
     if(nwindows == 0) {
@@ -63,7 +55,8 @@
     }
 }
 
-static UiObject* create_window(char *title, void *window_data, UiBool simple) {
+
+static UiObject* create_window(const char *title, void *window_data, Boolean simple) {
     CxMempool *mp = cxBasicMempoolCreate(256);
     const CxAllocator *a = mp->allocator;
     UiObject *obj = cxCalloc(a, 1, sizeof(UiObject));
@@ -72,23 +65,20 @@
     
     Arg args[16];
     int n = 0;
-    
-    XtSetArg(args[0], XmNtitle, title);
-    //XtSetArg(args[1], XmNbaseWidth, window_default_width);
-    //XtSetArg(args[2], XmNbaseHeight, window_default_height);
-    XtSetArg(args[1], XmNminWidth, 100);
-    XtSetArg(args[2], XmNminHeight, 50);
-    XtSetArg(args[3], XmNwidth, window_default_width);
-    XtSetArg(args[4], XmNheight, window_default_height);
+    XtSetArg(args[n], XmNtitle, title); n++;
+    XtSetArg(args[n], XmNminWidth, 100); n++;
+    XtSetArg(args[n], XmNminHeight, 50); n++;
+    XtSetArg(args[n], XmNwidth, window_default_width); n++;
+    XtSetArg(args[n], XmNheight, window_default_height); n++;
     
     Widget toplevel = XtAppCreateShell(
-            "Test123",
-            "abc",
+            ui_appname(),
+            "mainwindow",
             //applicationShellWidgetClass,
             vendorShellWidgetClass,
-            ui_get_display(),
+            ui_motif_get_display(),
             args,
-            5);
+            n);
     
     Atom wm_delete_window;
     wm_delete_window = XmInternAtom(
@@ -101,111 +91,30 @@
             window_close_handler,
             obj);
     
-    // TODO: use callback
-    ui_set_active_window(toplevel);
-    
     Widget window = XtVaCreateManagedWidget(
             title,
             xmMainWindowWidgetClass,
             toplevel,
             NULL);
-    obj->widget = window;
-    Widget form = XtVaCreateManagedWidget(
-            "window_form",
-            xmFormWidgetClass,
-            window,
-            NULL);
-    Widget toolbar = NULL;
     
-    if(!simple) {
-        ui_create_menubar(obj);
-        toolbar = ui_create_toolbar(obj, form);
-    }
-    
-    // window content
-    XtSetArg(args[0], XmNshadowType, XmSHADOW_ETCHED_OUT);
-    XtSetArg(args[1], XmNshadowThickness, 0);
-    XtSetArg(args[2], XmNleftAttachment, XmATTACH_FORM);
-    XtSetArg(args[3], XmNrightAttachment, XmATTACH_FORM);
-    XtSetArg(args[4], XmNbottomAttachment, XmATTACH_FORM);
-    if(toolbar) {
-        XtSetArg(args[5], XmNtopAttachment, XmATTACH_WIDGET);
-        XtSetArg(args[6], XmNtopWidget, toolbar);
-        n = 7;
-    } else {
-        XtSetArg(args[5], XmNtopAttachment, XmATTACH_FORM);
-        n = 6;
-    }
-    Widget frame = XmCreateFrame(form, "content_frame", args, n);
+    // content frame
+    n = 0;
+    Widget frame = XmCreateFrame(window, "window_frame", args, n);
     XtManageChild(frame);
     
-    Widget content_form = XmCreateForm(frame, "content_form", NULL, 0);
-    XtManageChild(content_form);
-    obj->container = ui_box_container(obj, content_form, 0, 0, UI_BOX_VERTICAL);
+    Widget vbox = XtCreateManagedWidget("window_vbox", gridClass, frame, NULL, 0);
+    UiContainerX *container = ui_box_container(obj, vbox, UI_BOX_VERTICAL);
+    uic_object_push_container(obj, container);
     
-    XtManageChild(form);
-      
     obj->widget = toplevel;
     nwindows++;
     return obj;
-}
+} 
 
-UiObject* ui_window(char *title, void *window_data) {
+UiObject* ui_window(const char *title, void *window_data) {
     return create_window(title, window_data, FALSE);
 }
 
-UiObject* ui_simplewindow(char *title, void *window_data) {
+UiObject* ui_simple_window(const char *title, void *window_data) {
     return create_window(title, window_data, TRUE);
 }
-
-void ui_close(UiObject *obj) {
-    XtDestroyWidget(obj->widget);
-    window_close_handler(obj->widget, obj, NULL);
-}
-
-typedef struct FileDialogData {
-    int  running;
-    char *file;
-} FileDialogData;
-
-static void filedialog_select(
-        Widget widget,
-        FileDialogData *data,
-        XmFileSelectionBoxCallbackStruct *selection)
-{
-    char *path = NULL;
-    XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &path);
-    data->running = 0;
-    data->file = strdup(path);
-    XtFree(path);
-    XtUnmanageChild(widget);
-}
-
-static void filedialog_cancel(
-        Widget widget,
-        FileDialogData *data,
-        XmFileSelectionBoxCallbackStruct *selection)
-
-{
-    data->running = 0;
-    XtUnmanageChild(widget);
-}
-
-char* ui_openfiledialog(UiObject *obj) {
-    Widget dialog = XmCreateFileSelectionDialog(obj->widget, "openfiledialog", NULL, 0);
-    XtManageChild(dialog);
-    
-    FileDialogData data;
-    data.running = 1;
-    data.file = NULL;
-    
-    XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, &data);
-    XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_cancel, &data);
-    
-    ui_secondary_event_loop(&data.running);
-    return data.file;
-}
-
-char* ui_savefiledialog(UiObject *obj) {
-    return ui_openfiledialog(obj);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/motif/window.h	Wed Dec 04 18:31:22 2024 +0100
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WINDOW_H */
+
--- a/ui/ui/toolkit.h	Wed Dec 04 08:57:35 2024 +0100
+++ b/ui/ui/toolkit.h	Wed Dec 04 18:31:22 2024 +0100
@@ -441,7 +441,7 @@
 UIEXPORT void ui_context_destroy(UiContext *ctx);
 
 UIEXPORT void ui_object_ref(UiObject *obj);
-UIEXPORT void ui_object_unref(UiObject *obj);
+UIEXPORT int ui_object_unref(UiObject *obj);
 
 UIEXPORT void ui_onstartup(ui_callback f, void *userdata);
 UIEXPORT void ui_onopen(ui_callback f, void *userdata);

mercurial