initial newapi GTK port newapi

Sun, 11 Feb 2024 22:06:23 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 11 Feb 2024 22:06:23 +0100
branch
newapi
changeset 253
087cc9216f28
parent 252
7d176764756d
child 254
13997c76859b

initial newapi GTK port

application/main.c file | annotate | diff | comparison | revisions
ucx/Makefile file | annotate | diff | comparison | revisions
ucx/array_list.c file | annotate | diff | comparison | revisions
ucx/buffer.c file | annotate | diff | comparison | revisions
ucx/compare.c file | annotate | diff | comparison | revisions
ucx/cx/array_list.h file | annotate | diff | comparison | revisions
ucx/cx/buffer.h file | annotate | diff | comparison | revisions
ucx/cx/collection.h file | annotate | diff | comparison | revisions
ucx/cx/common.h file | annotate | diff | comparison | revisions
ucx/cx/compare.h file | annotate | diff | comparison | revisions
ucx/cx/hash_key.h file | annotate | diff | comparison | revisions
ucx/cx/hash_map.h file | annotate | diff | comparison | revisions
ucx/cx/iterator.h file | annotate | diff | comparison | revisions
ucx/cx/linked_list.h file | annotate | diff | comparison | revisions
ucx/cx/list.h file | annotate | diff | comparison | revisions
ucx/cx/map.h file | annotate | diff | comparison | revisions
ucx/cx/mempool.h file | annotate | diff | comparison | revisions
ucx/cx/printf.h file | annotate | diff | comparison | revisions
ucx/cx/string.h file | annotate | diff | comparison | revisions
ucx/cx/test.h file | annotate | diff | comparison | revisions
ucx/cx/tree.h file | annotate | diff | comparison | revisions
ucx/cx/utils.h file | annotate | diff | comparison | revisions
ucx/linked_list.c file | annotate | diff | comparison | revisions
ucx/list.c file | annotate | diff | comparison | revisions
ucx/printf.c file | annotate | diff | comparison | revisions
ucx/string.c file | annotate | diff | comparison | revisions
ucx/tree.c file | annotate | diff | comparison | revisions
ui/gtk/button.c file | annotate | diff | comparison | revisions
ui/gtk/container.c file | annotate | diff | comparison | revisions
ui/gtk/container.h file | annotate | diff | comparison | revisions
ui/gtk/dnd.c file | annotate | diff | comparison | revisions
ui/gtk/image.c file | annotate | diff | comparison | revisions
ui/gtk/menu.c file | annotate | diff | comparison | revisions
ui/gtk/model.c file | annotate | diff | comparison | revisions
ui/gtk/text.c file | annotate | diff | comparison | revisions
ui/gtk/text.h file | annotate | diff | comparison | revisions
ui/gtk/toolbar.c file | annotate | diff | comparison | revisions
ui/gtk/toolkit.c file | annotate | diff | comparison | revisions
ui/gtk/tree.c file | annotate | diff | comparison | revisions
ui/gtk/window.c file | annotate | diff | comparison | revisions
ui/motif/container.c 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/window.c file | annotate | diff | comparison | revisions
ui/ui/toolkit.h file | annotate | diff | comparison | revisions
--- a/application/main.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/application/main.c	Sun Feb 11 22:06:23 2024 +0100
@@ -34,7 +34,8 @@
 #include <cx/utils.h>
 
 typedef struct {
-    UiText *text;
+    UiString *str1;
+    UiString *str2;
 } MyDocument;
 
 MyDocument *doc1;
@@ -42,48 +43,31 @@
 
 
 void action_menu(UiEvent *event, void *userdata) {
-    printf("action_menu: %s\n", (char*)userdata);
+    
 }
 
 void action_button(UiEvent *event, void *userdata) {
-    printf("button test\n");
-    MyDocument *doc = event->document;
-    if(!doc) {
-        printf("no document\n");
-        return;
-    }
     
-    char *text = doc->text->get(doc->text);
-    printf("text: {\n%s\n}\n", text);
 }
 
 void action_switch(UiEvent *event, void *userdata) {
-    if(event->document == doc1) {
-        ui_set_document(event->obj, doc2);
-    } else {
-        ui_set_document(event->obj, doc1);
-    }
+    
 }
 
 
 MyDocument* create_doc(void) {
     MyDocument *doc = ui_document_new(sizeof(MyDocument));
     UiContext *docctx = ui_document_context(doc);
-    doc->text = ui_text_new(docctx, "text");
+    doc->str1 = ui_string_new(docctx, "str1");
+    doc->str1 = ui_string_new(docctx, "str2");
     return doc;
 }
 
 void application_startup(UiEvent *event, void *data) {
     
     UiObject *obj = ui_window("Test", NULL);
-    ui_textarea_nv(obj, "text");
-    ui_button(obj, "Test", action_button, NULL);
-    ui_button(obj, "Switch Document", action_switch, NULL);
     
-    doc1 = create_doc();
-    doc2 = create_doc();
     
-    ui_attach_document(obj->ctx, doc1);
     
     ui_show(obj);
 }
@@ -93,15 +77,7 @@
     ui_onstartup(application_startup, NULL);
     
     // menu
-    ui_menu("_File");
-    ui_menuitem("_Hello", action_menu, NULL);
-    ui_submenu("Submenu1");
-    ui_submenu("Submenu2");
-    ui_menuitem("item2", action_menu, NULL);
-    ui_submenu_end();
-    ui_menuitem("item3", action_menu, NULL);
-    ui_submenu_end();
-    ui_menuitem("item4", action_menu, NULL);
+    
 
     
     ui_main();
--- a/ucx/Makefile	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/Makefile	Sun Feb 11 22:06:23 2024 +0100
@@ -32,7 +32,7 @@
 # list of source files
 SRC  = allocator.c
 SRC += array_list.c
-SRC += basic_mempool.c
+SRC += mempool.c
 SRC += buffer.c
 SRC += compare.c
 SRC += hash_key.c
--- a/ucx/array_list.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/array_list.c	Sun Feb 11 22:06:23 2024 +0100
@@ -27,12 +27,30 @@
  */
 
 #include "cx/array_list.h"
+#include "cx/compare.h"
 #include <assert.h>
 #include <string.h>
 
+// Default array reallocator
+
+static void *cx_array_default_realloc(
+        void *array,
+        size_t capacity,
+        size_t elem_size,
+        __attribute__((__unused__)) struct cx_array_reallocator_s *alloc
+) {
+    return realloc(array, capacity * elem_size);
+}
+
+struct cx_array_reallocator_s cx_array_default_reallocator_impl = {
+        cx_array_default_realloc, NULL, NULL, 0, 0
+};
+
+struct cx_array_reallocator_s *cx_array_default_reallocator = &cx_array_default_reallocator_impl;
+
 // LOW LEVEL ARRAY LIST FUNCTIONS
 
-enum cx_array_copy_result cx_array_copy(
+enum cx_array_result cx_array_copy(
         void **target,
         size_t *size,
         size_t *capacity,
@@ -59,7 +77,7 @@
     if (needrealloc) {
         // a reallocator and a capacity variable must be available
         if (reallocator == NULL || capacity == NULL) {
-            return CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED;
+            return CX_ARRAY_REALLOC_NOT_SUPPORTED;
         }
 
         // check, if we need to repair the src pointer
@@ -77,7 +95,7 @@
                 *target, cap, elem_size, reallocator
         );
         if (newmem == NULL) {
-            return CX_ARRAY_COPY_REALLOC_FAILED;
+            return CX_ARRAY_REALLOC_FAILED;
         }
 
         // repair src pointer, if necessary
@@ -99,12 +117,13 @@
     *size = newsize;
 
     // return successfully
-    return CX_ARRAY_COPY_SUCCESS;
+    return CX_ARRAY_SUCCESS;
 }
 
 #ifndef CX_ARRAY_SWAP_SBO_SIZE
 #define CX_ARRAY_SWAP_SBO_SIZE 128
 #endif
+unsigned cx_array_swap_sbo_size = CX_ARRAY_SWAP_SBO_SIZE;
 
 void cx_array_swap(
         void *arr,
@@ -208,7 +227,7 @@
         size_t elems_to_move = list->size - index;
         size_t start_of_moved = index + n;
 
-        if (CX_ARRAY_COPY_SUCCESS != cx_array_copy(
+        if (CX_ARRAY_SUCCESS != cx_array_copy(
                 &arl->data,
                 &list->size,
                 &arl->capacity,
@@ -228,7 +247,7 @@
     // therefore, it is impossible to leave this function with an invalid array
 
     // place the new elements
-    if (CX_ARRAY_COPY_SUCCESS == cx_array_copy(
+    if (CX_ARRAY_SUCCESS == cx_array_copy(
             &arl->data,
             &list->size,
             &arl->capacity,
@@ -308,11 +327,14 @@
             list->size - index - 1,
             &arl->reallocator
     );
-    if (result == 0) {
-        // decrease the size
-        list->size--;
-    }
-    return result;
+
+    // cx_array_copy cannot fail, array cannot grow
+    assert(result == 0);
+
+    // decrease the size
+    list->size--;
+
+    return 0;
 }
 
 static void cx_arl_clear(struct cx_list_s *list) {
@@ -362,9 +384,10 @@
     }
 }
 
-static ssize_t cx_arl_find(
-        struct cx_list_s const *list,
-        void const *elem
+static ssize_t cx_arl_find_remove(
+        struct cx_list_s *list,
+        void const *elem,
+        bool remove
 ) {
     assert(list->cmpfunc != NULL);
     assert(list->size < SIZE_MAX / 2);
@@ -372,7 +395,15 @@
 
     for (ssize_t i = 0; i < (ssize_t) list->size; i++) {
         if (0 == list->cmpfunc(elem, cur)) {
-            return i;
+            if (remove) {
+                if (0 == cx_arl_remove(list, i)) {
+                    return i;
+                } else {
+                    return -1;
+                }
+            } else {
+                return i;
+            }
         }
         cur += list->item_size;
     }
@@ -500,7 +531,7 @@
         cx_arl_clear,
         cx_arl_swap,
         cx_arl_at,
-        cx_arl_find,
+        cx_arl_find_remove,
         cx_arl_sort,
         cx_arl_compare,
         cx_arl_reverse,
@@ -522,13 +553,14 @@
 
     list->base.cl = &cx_array_list_class;
     list->base.allocator = allocator;
-    list->base.cmpfunc = comparator;
     list->capacity = initial_capacity;
 
     if (item_size > 0) {
         list->base.item_size = item_size;
+        list->base.cmpfunc = comparator;
     } else {
         item_size = sizeof(void *);
+        list->base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator;
         cxListStorePointers((CxList *) list);
     }
 
--- a/ucx/buffer.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/buffer.c	Sun Feb 11 22:06:23 2024 +0100
@@ -135,6 +135,11 @@
     buffer->pos = 0;
 }
 
+void cxBufferReset(CxBuffer *buffer) {
+    buffer->size = 0;
+    buffer->pos = 0;
+}
+
 int cxBufferEof(CxBuffer const *buffer) {
     return buffer->pos >= buffer->size;
 }
--- a/ucx/compare.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/compare.c	Sun Feb 11 22:06:23 2024 +0100
@@ -199,3 +199,15 @@
     }
 }
 
+int cx_cmp_ptr(
+        void const *ptr1,
+        void const *ptr2
+) {
+    uintptr_t p1 = (uintptr_t) ptr1;
+    uintptr_t p2 = (uintptr_t) ptr2;
+    if (p1 == p2) {
+        return 0;
+    } else {
+        return p1 < p2 ? -1 : 1;
+    }
+}
--- a/ucx/cx/array_list.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/array_list.h	Sun Feb 11 22:06:23 2024 +0100
@@ -31,7 +31,6 @@
  * \details Also provides several low-level functions for custom array list implementations.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
@@ -46,6 +45,11 @@
 #endif
 
 /**
+ * The maximum item size in an array list that fits into stack buffer when swapped.
+ */
+extern unsigned cx_array_swap_sbo_size;
+
+/**
  * Defines a reallocation mechanism for arrays.
  */
 struct cx_array_reallocator_s {
@@ -54,8 +58,9 @@
      *
      * Implementations are not required to free the original array.
      * This allows re-allocation of static memory by allocating heap memory
-     * and copying the array contents. The information in \p data can keep
-     * track of the state of the memory or other additional allocator info.
+     * and copying the array contents. The information in the custom fields of
+     * the referenced allocator can be used to track the state of the memory
+     * or to transport other additional data.
      *
      * @param array the array to reallocate
      * @param capacity the new capacity (number of elements)
@@ -89,12 +94,17 @@
 };
 
 /**
- * Return codes for cx_array_copy().
+ * A default stdlib-based array reallocator.
  */
-enum cx_array_copy_result {
-    CX_ARRAY_COPY_SUCCESS,
-    CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED,
-    CX_ARRAY_COPY_REALLOC_FAILED,
+extern struct cx_array_reallocator_s *cx_array_default_reallocator;
+
+/**
+ * Return codes for array functions.
+ */
+enum cx_array_result {
+    CX_ARRAY_SUCCESS,
+    CX_ARRAY_REALLOC_NOT_SUPPORTED,
+    CX_ARRAY_REALLOC_FAILED,
 };
 
 /**
@@ -107,7 +117,7 @@
  * capacity is used.
  *
  * If the capacity is insufficient to hold the new data, a reallocation
- * attempt is made, unless the allocator is set to \c NULL, in which case
+ * attempt is made, unless the \p reallocator is set to \c NULL, in which case
  * this function ultimately returns a failure.
  *
  * @param target the target array
@@ -122,7 +132,7 @@
  * if re-allocation shall not happen
  * @return zero on success, non-zero error code on failure
  */
-enum cx_array_copy_result cx_array_copy(
+enum cx_array_result cx_array_copy(
         void **target,
         size_t *size,
         size_t *capacity,
@@ -133,6 +143,28 @@
         struct cx_array_reallocator_s *reallocator
 ) __attribute__((__nonnull__(1, 2, 5)));
 
+/**
+ * Adds an element to an array with the possibility of allocating more space.
+ *
+ * The element \p elem is added to the end of the \p target array which containing
+ * \p size elements, already. The \p capacity must not be \c NULL and point a
+ * variable holding the current maximum number of elements the array can hold.
+ *
+ * If the capacity is insufficient to hold the new element, and the optional
+ * \p reallocator is not \c NULL, an attempt increase the \p capacity is made
+ * and the new capacity is written back.
+ *
+ * @param target the target array
+ * @param size a pointer to the size of the target array
+ * @param capacity a pointer to the target array's capacity - must not be \c NULL
+ * @param elem_size the size of one element
+ * @param elem the element to add
+ * @param reallocator the array re-allocator to use, or \c NULL
+ * if re-allocation shall not happen
+ * @return zero on success, non-zero error code on failure
+ */
+#define cx_array_add(target, size, capacity, elem_size, elem, reallocator) \
+    cx_array_copy((void**)(target), size, capacity, *(size), elem, elem_size, 1, reallocator)
 
 /**
  * Swaps two array elements.
@@ -153,12 +185,14 @@
  * Allocates an array list for storing elements with \p item_size bytes each.
  *
  * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
- * cxListStorePointers() was called immediately after creation.
+ * cxListStorePointers() was called immediately after creation and the compare
+ * function will be automatically set to cx_cmp_ptr(), if none is given.
  *
  * @param allocator the allocator for allocating the list memory
  * (if \c NULL the cxDefaultAllocator will be used)
  * @param comparator the comparator for the elements
- * (if \c NULL sort and find functions will not work)
+ * (if \c NULL, and the list is not storing pointers, sort and find
+ * functions will not work)
  * @param item_size the size of each element in bytes
  * @param initial_capacity the initial number of elements the array can store
  * @return the created list
@@ -178,7 +212,8 @@
  * set it immediately after creation or use cxArrayListCreate().
  *
  * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
- * cxListStorePointers() was called immediately after creation.
+ * cxListStorePointers() was called immediately after creation and the compare
+ * function will be automatically set to cx_cmp_ptr().
  *
  * @param item_size the size of each element in bytes
  * @param initial_capacity the initial number of elements the array can store
--- a/ucx/cx/buffer.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/buffer.h	Sun Feb 11 22:06:23 2024 +0100
@@ -40,7 +40,6 @@
  *
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
@@ -312,18 +311,32 @@
  * Clears the buffer by resetting the position and deleting the data.
  *
  * The data is deleted by zeroing it with a call to memset().
+ * If you do not need that, you can use the faster cxBufferReset().
  *
  * @param buffer the buffer to be cleared
+ * @see cxBufferReset()
  */
 __attribute__((__nonnull__))
 void cxBufferClear(CxBuffer *buffer);
 
 /**
- * Tests, if the buffer position has exceeded the buffer capacity.
+ * Resets the buffer by resetting the position and size to zero.
+ *
+ * The data in the buffer is not deleted. If you need a safe
+ * reset of the buffer, use cxBufferClear().
+ *
+ * @param buffer the buffer to be cleared
+ * @see cxBufferClear()
+ */
+__attribute__((__nonnull__))
+void cxBufferReset(CxBuffer *buffer);
+
+/**
+ * Tests, if the buffer position has exceeded the buffer size.
  *
  * @param buffer the buffer to test
  * @return non-zero, if the current buffer position has exceeded the last
- * available byte of the buffer.
+ * byte of the buffer's contents.
  */
 __attribute__((__nonnull__))
 int cxBufferEof(CxBuffer const *buffer);
--- a/ucx/cx/collection.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/collection.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief Common definitions for various collection implementations.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
@@ -49,6 +48,8 @@
  */
 #define CX_STORE_POINTERS 0
 
+#ifndef CX_COMPARE_FUNC_DEFINED
+#define CX_COMPARE_FUNC_DEFINED
 /**
  * A comparator function comparing two collection elements.
  */
@@ -56,6 +57,7 @@
         void const *left,
         void const *right
 );
+#endif // CX_COMPARE_FUNC_DEFINED
 
 /**
  * Use this macro to declare common members for a collection structure.
--- a/ucx/cx/common.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/common.h	Sun Feb 11 22:06:23 2024 +0100
@@ -33,7 +33,6 @@
  *
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  *
  * \mainpage UAP Common Extensions
@@ -84,7 +83,7 @@
 #define UCX_VERSION_MAJOR   3
 
 /** Minor UCX version as integer constant. */
-#define UCX_VERSION_MINOR   0
+#define UCX_VERSION_MINOR   1
 
 /** Version constant which ensures to increase monotonically. */
 #define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR)
@@ -97,6 +96,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#ifndef UCX_TEST_H
 /**
  * Function pointer compatible with fwrite-like functions.
  */
@@ -106,6 +106,7 @@
         size_t,
         void *
 );
+#endif // UCX_TEST_H
 
 /**
  * Function pointer compatible with fread-like functions.
--- a/ucx/cx/compare.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/compare.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief A collection of simple compare functions.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
@@ -43,6 +42,17 @@
 extern "C" {
 #endif
 
+#ifndef CX_COMPARE_FUNC_DEFINED
+#define CX_COMPARE_FUNC_DEFINED
+/**
+ * A comparator function comparing two collection elements.
+ */
+typedef int(*cx_compare_func)(
+        void const *left,
+        void const *right
+);
+#endif // CX_COMPARE_FUNC_DEFINED
+
 /**
  * Compares two integers of type int.
  *
@@ -213,6 +223,19 @@
         void const *ptr2
 );
 
+/**
+ * Compares the pointers specified in the arguments without de-referencing.
+ *
+ * @param ptr1 pointer one
+ * @param ptr2 pointer two
+ * @return -1 if ptr1 is less than ptr2, 0 if both are equal,
+ * 1 if ptr1 is greater than ptr2
+ */
+int cx_cmp_ptr(
+        void const *ptr1,
+        void const *ptr2
+);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
--- a/ucx/cx/hash_key.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/hash_key.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief Interface for map implementations.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
--- a/ucx/cx/hash_map.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/hash_map.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief Hash map implementation.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
--- a/ucx/cx/iterator.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/iterator.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief Interface for iterator implementations.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
--- a/ucx/cx/linked_list.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/linked_list.h	Sun Feb 11 22:06:23 2024 +0100
@@ -31,7 +31,6 @@
  * \details Also provides several low-level functions for custom linked list implementations.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
@@ -46,21 +45,22 @@
 #endif
 
 /**
- * Set this flag to true, if you want to disable the use of SBO for
- * linked list swap operations.
+ * The maximum item size that uses SBO swap instead of relinking.
  */
-extern bool CX_DISABLE_LINKED_LIST_SWAP_SBO;
+extern unsigned cx_linked_list_swap_sbo_size;
 
 /**
  * Allocates a linked list for storing elements with \p item_size bytes each.
  *
  * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
- * cxListStorePointers() was called immediately after creation.
+ * cxListStorePointers() was called immediately after creation and the compare
+ * function will be automatically set to cx_cmp_ptr(), if none is given.
  *
  * @param allocator the allocator for allocating the list nodes
  * (if \c NULL the cxDefaultAllocator will be used)
  * @param comparator the comparator for the elements
- * (if \c NULL sort and find functions will not work)
+ * (if \c NULL, and the list is not storing pointers, sort and find
+ * functions will not work)
  * @param item_size the size of each element in bytes
  * @return the created list
  */
@@ -78,7 +78,8 @@
  * after list creation or use cxLinkedListCreate().
  *
  * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
- * cxListStorePointers() was called immediately after creation.
+ * cxListStorePointers() was called immediately after creation and the compare
+ * function will be automatically set to cx_cmp_ptr().
  *
  * @param item_size the size of each element in bytes
  * @return the created list
@@ -129,6 +130,27 @@
 ) __attribute__((__nonnull__));
 
 /**
+ * Finds the node containing an element within a linked list.
+ *
+ * @param result a pointer to the memory where the node pointer (or \c NULL if the element
+ * could not be found) shall be stored to
+ * @param start a pointer to the start node
+ * @param loc_advance the location of the pointer to advance
+ * @param loc_data the location of the \c data pointer within your node struct
+ * @param cmp_func a compare function to compare \p elem against the node data
+ * @param elem a pointer to the element to find
+ * @return the index of the element or a negative value if it could not be found
+ */
+ssize_t cx_linked_list_find_node(
+        void **result,
+        void const *start,
+        ptrdiff_t loc_advance,
+        ptrdiff_t loc_data,
+        cx_compare_func cmp_func,
+        void const *elem
+) __attribute__((__nonnull__));
+
+/**
  * Finds the first node in a linked list.
  *
  * The function starts with the pointer denoted by \p node and traverses the list
--- a/ucx/cx/list.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/list.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief Interface for list implementations.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
@@ -137,11 +136,12 @@
     );
 
     /**
-     * Member function for finding an element.
+     * Member function for finding and optionally removing an element.
      */
-    ssize_t (*find)(
-            struct cx_list_s const *list,
-            void const *elem
+    ssize_t (*find_remove)(
+            struct cx_list_s *list,
+            void const *elem,
+            bool remove
     );
 
     /**
@@ -580,7 +580,25 @@
         CxList const *list,
         void const *elem
 ) {
-    return list->cl->find(list, elem);
+    return list->cl->find_remove((CxList*)list, elem, false);
+}
+
+/**
+ * Removes and returns the index of the first element that equals \p elem.
+ *
+ * Determining equality is performed by the list's comparator function.
+ *
+ * @param list the list
+ * @param elem the element to find and remove
+ * @return the index of the now removed element or a negative
+ * value when the element is not found or could not be removed
+ */
+__attribute__((__nonnull__))
+static inline ssize_t cxListFindRemove(
+        CxList *list,
+        void const *elem
+) {
+    return list->cl->find_remove(list, elem, true);
 }
 
 /**
--- a/ucx/cx/map.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/map.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief Interface for map implementations.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
--- a/ucx/cx/mempool.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/mempool.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief Interface for memory pool implementations.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
--- a/ucx/cx/printf.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/printf.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief Wrapper for write functions with a printf-like interface.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
@@ -45,6 +44,12 @@
 extern "C" {
 #endif
 
+
+/**
+ * The maximum string length that fits into stack memory.
+ */
+extern unsigned const cx_printf_sbo_size;
+
 /**
  * A \c fprintf like function which writes the output to a stream by
  * using a write_func.
@@ -159,6 +164,170 @@
 #define cx_bprintf(buffer, fmt, ...) cx_fprintf((CxBuffer*)buffer, \
     (cx_write_func) cxBufferWrite, fmt, __VA_ARGS__)
 
+
+/**
+ * An \c sprintf like function which reallocates the string when the buffer is not large enough.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ * That means, when the buffer needed to be reallocated, the new size of the buffer will be
+ * the length returned by this function plus one.
+ *
+ * @param str a pointer to the string buffer
+ * @param len the current length of the buffer
+ * @param fmt the format string
+ * @param ... additional arguments
+ * @return the length of produced string
+ */
+#define cx_sprintf(str, len, fmt, ...) cx_sprintf_a(cxDefaultAllocator, str, len, fmt, __VA_ARGS__)
+
+/**
+ * An \c sprintf like function which reallocates the string when the buffer is not large enough.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ * That means, when the buffer needed to be reallocated, the new size of the buffer will be
+ * the length returned by this function plus one.
+ *
+ * \attention The original buffer MUST have been allocated with the same allocator!
+ *
+ * @param alloc the allocator to use
+ * @param str a pointer to the string buffer
+ * @param len the current length of the buffer
+ * @param fmt the format string
+ * @param ... additional arguments
+ * @return the length of produced string
+ */
+__attribute__((__nonnull__(1, 2, 4), __format__(printf, 4, 5)))
+int cx_sprintf_a(CxAllocator *alloc, char **str, size_t len, const char *fmt, ... );
+
+
+/**
+ * An \c sprintf like function which reallocates the string when the buffer is not large enough.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ * That means, when the buffer needed to be reallocated, the new size of the buffer will be
+ * the length returned by this function plus one.
+ *
+ * @param str a pointer to the string buffer
+ * @param len the current length of the buffer
+ * @param fmt the format string
+ * @param ap argument list
+ * @return the length of produced string
+ */
+#define cx_vsprintf(str, len, fmt, ap) cx_vsprintf_a(cxDefaultAllocator, str, len, fmt, ap)
+
+/**
+ * An \c sprintf like function which reallocates the string when the buffer is not large enough.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ * That means, when the buffer needed to be reallocated, the new size of the buffer will be
+ * the length returned by this function plus one.
+ *
+ * \attention The original buffer MUST have been allocated with the same allocator!
+ *
+ * @param alloc the allocator to use
+ * @param str a pointer to the string buffer
+ * @param len the current length of the buffer
+ * @param fmt the format string
+ * @param ap argument list
+ * @return the length of produced string
+ */
+__attribute__((__nonnull__))
+int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t len, const char *fmt, va_list ap);
+
+
+/**
+ * An \c sprintf like function which allocates a new string when the buffer is not large enough.
+ *
+ * The location of the resulting string will \em always be stored to \p str. When the buffer
+ * was sufficiently large, \p buf itself will be stored to the location of \p str.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ * That means, when the buffer needed to be reallocated, the new size of the buffer will be
+ * the length returned by this function plus one.
+ * 
+ * \remark When a new string needed to be allocated, the contents of \p buf will be
+ * poisoned after the call, because this function tries to produce the string in \p buf, first.
+ *
+ * @param buf a pointer to the buffer
+ * @param len the length of the buffer
+ * @param str a pointer to the location
+ * @param fmt the format string
+ * @param ... additional arguments
+ * @return the length of produced string
+ */
+#define cx_sprintf_s(buf, len, str, fmt, ...) cx_sprintf_sa(cxDefaultAllocator, buf, len, str, fmt, __VA_ARGS__)
+
+/**
+ * An \c sprintf like function which allocates a new string when the buffer is not large enough.
+ *
+ * The location of the resulting string will \em always be stored to \p str. When the buffer
+ * was sufficiently large, \p buf itself will be stored to the location of \p str.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ * That means, when the buffer needed to be reallocated, the new size of the buffer will be
+ * the length returned by this function plus one.
+ *
+ * \remark When a new string needed to be allocated, the contents of \p buf will be
+ * poisoned after the call, because this function tries to produce the string in \p buf, first.
+ *
+ * @param alloc the allocator to use
+ * @param buf a pointer to the buffer
+ * @param len the length of the buffer
+ * @param str a pointer to the location
+ * @param fmt the format string
+ * @param ... additional arguments
+ * @return the length of produced string
+ */
+__attribute__((__nonnull__(1, 2, 4, 5), __format__(printf, 5, 6)))
+int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t len, char **str, const char *fmt, ... );
+
+/**
+ * An \c sprintf like function which allocates a new string when the buffer is not large enough.
+ *
+ * The location of the resulting string will \em always be stored to \p str. When the buffer
+ * was sufficiently large, \p buf itself will be stored to the location of \p str.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ * That means, when the buffer needed to be reallocated, the new size of the buffer will be
+ * the length returned by this function plus one.
+ *
+ * \remark When a new string needed to be allocated, the contents of \p buf will be
+ * poisoned after the call, because this function tries to produce the string in \p buf, first.
+ *
+ * @param buf a pointer to the buffer
+ * @param len the length of the buffer
+ * @param str a pointer to the location
+ * @param fmt the format string
+ * @param ap argument list
+ * @return the length of produced string
+ */
+#define cx_vsprintf_s(buf, len, str, fmt, ap) cx_vsprintf_sa(cxDefaultAllocator, buf, len, str, fmt, ap)
+
+/**
+ * An \c sprintf like function which allocates a new string when the buffer is not large enough.
+ *
+ * The location of the resulting string will \em always be stored to \p str. When the buffer
+ * was sufficiently large, \p buf itself will be stored to the location of \p str.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ * That means, when the buffer needed to be reallocated, the new size of the buffer will be
+ * the length returned by this function plus one.
+ *
+ * \remark When a new string needed to be allocated, the contents of \p buf will be
+ * poisoned after the call, because this function tries to produce the string in \p buf, first.
+ *
+ * @param alloc the allocator to use
+ * @param buf a pointer to the buffer
+ * @param len the length of the buffer
+ * @param str a pointer to the location
+ * @param fmt the format string
+ * @param ap argument list
+ * @return the length of produced string
+ */
+__attribute__((__nonnull__))
+int cx_vsprintf_sa(CxAllocator *alloc, char *buf, size_t len, char **str, const char *fmt, va_list ap);
+
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
--- a/ucx/cx/string.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/string.h	Sun Feb 11 22:06:23 2024 +0100
@@ -30,7 +30,6 @@
  * \brief Strings that know their length.
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
@@ -41,6 +40,11 @@
 #include "allocator.h"
 
 /**
+ * The maximum length of the "needle" in cx_strstr() that can use SBO.
+ */
+extern unsigned const cx_strstr_sbo_size;
+
+/**
  * The UCX string structure.
  */
 struct cx_mutstr_s {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/test.h	Sun Feb 11 22:06:23 2024 +0100
@@ -0,0 +1,330 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, 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.
+ */
+ 
+/**
+ * @file: test.h
+ * 
+ * UCX Test Framework.
+ * 
+ * Usage of this test framework:
+ *
+ * **** IN HEADER FILE: ****
+ *
+ * <pre>
+ * CX_TEST(function_name);
+ * CX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
+ * </pre>
+ *
+ * **** IN SOURCE FILE: ****
+ * <pre>
+ * CX_TEST_SUBROUTINE(subroutine_name, paramlist) {
+ *   // tests with CX_TEST_ASSERT()
+ * }
+ * 
+ * CX_TEST(function_name) {
+ *   // memory allocation and other stuff here
+ *   #CX_TEST_DO {
+ *     // tests with CX_TEST_ASSERT() and/or
+ *     // calls with CX_TEST_CALL_SUBROUTINE() here
+ *   }
+ *   // cleanup of memory here
+ * }
+ * </pre>
+ * 
+ * @attention Do not call own functions within a test, that use
+ * CX_TEST_ASSERT() macros and are not defined by using CX_TEST_SUBROUTINE().
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ *
+ */
+
+#ifndef UCX_TEST_H
+#define	UCX_TEST_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#ifndef __FUNCTION__
+/**
+ * Alias for the <code>__func__</code> preprocessor macro.
+ * Some compilers use <code>__func__</code> and others use __FUNCTION__.
+ * We use __FUNCTION__ so we define it for those compilers which use
+ * <code>__func__</code>.
+ */
+#define __FUNCTION__ __func__
+#endif
+
+//
+#if !defined(__clang__) && __GNUC__ > 3
+#pragma GCC diagnostic ignored "-Wclobbered"
+#endif
+
+#ifndef UCX_COMMON_H
+/**
+ * Function pointer compatible with fwrite-like functions.
+ */
+typedef size_t (*cx_write_func)(
+        void const *,
+        size_t,
+        size_t,
+        void *
+);
+#endif // UCX_COMMON_H
+
+/** Type for the CxTestSuite. */
+typedef struct CxTestSuite CxTestSuite;
+
+/** Pointer to a test function. */
+typedef void(*CxTest)(CxTestSuite *, void *, cx_write_func);
+
+/** Type for the internal list of test cases. */
+typedef struct CxTestSet CxTestSet;
+
+/** Structure for the internal list of test cases. */
+struct CxTestSet {
+    
+    /** Test case. */
+    CxTest test;
+    
+    /** Pointer to the next list element. */
+    CxTestSet *next;
+};
+
+/**
+ * A test suite containing multiple test cases.
+ */
+struct CxTestSuite {
+    
+    /** The number of successful tests after the suite has been run. */
+    unsigned int success;
+    
+    /** The number of failed tests after the suite has been run. */
+    unsigned int failure;
+
+    /** The optional name of this test suite. */
+    char const *name;
+    
+    /**
+     * Internal list of test cases.
+     * Use cx_test_register() to add tests to this list.
+     */
+    CxTestSet *tests;
+};
+
+/**
+ * Creates a new test suite.
+ * @param name optional name of the suite
+ * @return a new test suite
+ */
+static inline CxTestSuite* cx_test_suite_new(char const *name) {
+    CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite));
+    if (suite != NULL) {
+        suite->name = name;
+        suite->success = 0;
+        suite->failure = 0;
+        suite->tests = NULL;
+    }
+
+    return suite;
+}
+
+/**
+ * Destroys a test suite.
+ * @param suite the test suite to destroy
+ */
+static inline void cx_test_suite_free(CxTestSuite* suite) {
+    CxTestSet *l = suite->tests;
+    while (l != NULL) {
+        CxTestSet *e = l;
+        l = l->next;
+        free(e);
+    }
+    free(suite);
+}
+
+/**
+ * Registers a test function with the specified test suite.
+ * 
+ * @param suite the suite, the test function shall be added to
+ * @param test the test function to register
+ * @return zero on success or non-zero on failure
+ */
+static inline int cx_test_register(CxTestSuite* suite, CxTest test) {
+    CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet));
+    if (t) {
+        t->test = test;
+        t->next = NULL;
+        if (suite->tests == NULL) {
+            suite->tests = t;
+        } else {
+            CxTestSet *last = suite->tests;
+            while (last->next) {
+                last = last->next;
+            }
+            last->next = t;
+        }
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+/**
+ * Runs a test suite and writes the test log to the specified stream.
+ * @param suite the test suite to run
+ * @param out_target the target buffer or file to write the output to
+ * @param out_writer the write function writing to \p out_target
+ */
+static inline void cx_test_run(CxTestSuite *suite,
+                               void *out_target, cx_write_func out_writer) {
+    if (suite->name == NULL) {
+        out_writer("*** Test Suite ***\n", 1, 19, out_target);
+    } else {
+        out_writer("*** Test Suite : ", 1, 17, out_target);
+        out_writer(suite->name, 1, strlen(suite->name), out_target);
+        out_writer(" ***\n", 1, 5, out_target);
+    }
+    suite->success = 0;
+    suite->failure = 0;
+    for (CxTestSet *elem = suite->tests; elem; elem = elem->next) {
+        elem->test(suite, out_target, out_writer);
+    }
+    out_writer("\nAll test completed.\n", 1, 21, out_target);
+    char total[80];
+    int len = snprintf(
+            total, 80,
+            "  Total:   %u\n  Success: %u\n  Failure: %u\n\n",
+            suite->success + suite->failure, suite->success, suite->failure
+    );
+    out_writer(total, 1, len, out_target);
+}
+
+/**
+ * Runs a test suite and writes the test log to the specified FILE stream.
+ * @param suite the test suite to run
+ * @param file the target file to write the output to
+ */
+#define cx_test_run_f(suite, file) cx_test_run(suite, (void*)file, (cx_write_func)fwrite)
+
+/**
+ * Runs a test suite and writes the test log to stdout.
+ * @param suite the test suite to run
+ */
+#define cx_test_run_stdout(suite) cx_test_run_f(suite, stdout)
+
+/**
+ * Macro for a #CxTest function header.
+ * 
+ * Use this macro to declare and/or define a #CxTest function.
+ * 
+ * @param name the name of the test function
+ */
+#define CX_TEST(name) void name(CxTestSuite* _suite_,void *_output_, cx_write_func _writefnc_)
+
+/**
+ * Defines the scope of a test.
+ * @attention Any CX_TEST_ASSERT() calls must be performed in scope of
+ * #CX_TEST_DO.
+ */
+#define CX_TEST_DO _writefnc_("Running ", 1, 8, _output_);\
+        _writefnc_(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\
+        _writefnc_("... ", 1, 4, _output_);\
+        jmp_buf _env_;\
+        for (unsigned int _cx_test_loop_ = 0 ;\
+             _cx_test_loop_ == 0 && !setjmp(_env_);\
+             _writefnc_("success.\n", 1, 9, _output_),\
+             _suite_->success++, _cx_test_loop_++)
+
+/**
+ * Checks a test assertion.
+ * If the assertion is correct, the test carries on. If the assertion is not
+ * correct, the specified message (terminated by a dot and a line break) is
+ * written to the test suites output stream.
+ * @param condition the condition to check
+ * @param message the message that shall be printed out on failure
+ */
+#define CX_TEST_ASSERTM(condition,message) if (!(condition)) { \
+        char const* _assert_msg_ = message; \
+        _writefnc_(_assert_msg_, 1, strlen(_assert_msg_), _output_); \
+        _writefnc_(".\n", 1, 2, _output_); \
+        _suite_->failure++; \
+        longjmp(_env_, 1);\
+    } (void) 0
+
+/**
+ * Checks a test assertion.
+ * If the assertion is correct, the test carries on. If the assertion is not
+ * correct, the specified message (terminated by a dot and a line break) is
+ * written to the test suites output stream.
+ * @param condition the condition to check
+ */
+#define CX_TEST_ASSERT(condition) CX_TEST_ASSERTM(condition, #condition " failed")
+
+/**
+ * Macro for a test subroutine function header.
+ * 
+ * Use this to declare and/or define a subroutine that can be called by using
+ * CX_TEST_CALL_SUBROUTINE().
+ * 
+ * @param name the name of the subroutine
+ * @param ... the parameter list
+ * 
+ * @see CX_TEST_CALL_SUBROUTINE()
+ */
+#define CX_TEST_SUBROUTINE(name,...) void name(CxTestSuite* _suite_,\
+        void *_output_, cx_write_func _writefnc_, jmp_buf _env_, __VA_ARGS__)
+
+/**
+ * Macro for calling a test subroutine.
+ * 
+ * Subroutines declared with CX_TEST_SUBROUTINE() can be called by using this
+ * macro.
+ * 
+ * @remark You may <b>only</b> call subroutines within a #CX_TEST_DO block.
+ * 
+ * @param name the name of the subroutine
+ * @param ... the argument list
+ * 
+ * @see CX_TEST_SUBROUTINE()
+ */
+#define CX_TEST_CALL_SUBROUTINE(name,...) \
+        name(_suite_,_output_,_writefnc_,_env_,__VA_ARGS__)
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_TEST_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/tree.h	Sun Feb 11 22:06:23 2024 +0100
@@ -0,0 +1,69 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 Mike Becker, 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.
+ */
+/**
+ * \file tree.h
+ * \brief Interface for tree implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_TREE_H
+#define UCX_TREE_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+__attribute__((__nonnull__))
+void cx_tree_link(
+        void * restrict parent,
+        void * restrict node,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+);
+
+__attribute__((__nonnull__))
+void cx_tree_unlink(
+        void *node,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //UCX_TREE_H
--- a/ucx/cx/utils.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/cx/utils.h	Sun Feb 11 22:06:23 2024 +0100
@@ -33,7 +33,6 @@
  *
  * \author Mike Becker
  * \author Olaf Wintermann
- * \version 3.0
  * \copyright 2-Clause BSD License
  */
 
--- a/ucx/linked_list.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/linked_list.c	Sun Feb 11 22:06:23 2024 +0100
@@ -28,6 +28,7 @@
 
 #include "cx/linked_list.h"
 #include "cx/utils.h"
+#include "cx/compare.h"
 #include <string.h>
 #include <assert.h>
 
@@ -63,6 +64,23 @@
         cx_compare_func cmp_func,
         void const *elem
 ) {
+    void *dummy;
+    return cx_linked_list_find_node(
+            &dummy, start,
+            loc_advance, loc_data,
+            cmp_func, elem
+    );
+}
+
+ssize_t cx_linked_list_find_node(
+        void **result,
+        void const *start,
+        ptrdiff_t loc_advance,
+        ptrdiff_t loc_data,
+        cx_compare_func cmp_func,
+        void const *elem
+) {
+    assert(result != NULL);
     assert(start != NULL);
     assert(loc_advance >= 0);
     assert(loc_data >= 0);
@@ -73,11 +91,13 @@
     do {
         void *current = ll_data(node);
         if (cmp_func(current, elem) == 0) {
+            *result = (void*) node;
             return index;
         }
         node = ll_advance(node);
         index++;
     } while (node != NULL);
+    *result = NULL;
     return -1;
 }
 
@@ -460,8 +480,6 @@
 
 // HIGH LEVEL LINKED LIST IMPLEMENTATION
 
-bool CX_DISABLE_LINKED_LIST_SWAP_SBO = false;
-
 typedef struct cx_linked_list_node cx_linked_list_node;
 struct cx_linked_list_node {
     cx_linked_list_node *prev;
@@ -609,6 +627,7 @@
 #ifndef CX_LINKED_LIST_SWAP_SBO_SIZE
 #define CX_LINKED_LIST_SWAP_SBO_SIZE 128
 #endif
+unsigned cx_linked_list_swap_sbo_size = CX_LINKED_LIST_SWAP_SBO_SIZE;
 
 static int cx_ll_swap(
         struct cx_list_s *list,
@@ -633,6 +652,7 @@
     if (left < mid && right < mid) {
         // case 1: both items left from mid
         nleft = cx_ll_node_at(ll, left);
+        assert(nleft != NULL);
         nright = nleft;
         for (size_t c = left; c < right; c++) {
             nright = nright->next;
@@ -640,6 +660,7 @@
     } else if (left >= mid && right >= mid) {
         // case 2: both items right from mid
         nright = cx_ll_node_at(ll, right);
+        assert(nright != NULL);
         nleft = nright;
         for (size_t c = right; c > left; c--) {
             nleft = nleft->prev;
@@ -686,7 +707,7 @@
         }
     }
 
-    if (list->item_size > CX_LINKED_LIST_SWAP_SBO_SIZE || CX_DISABLE_LINKED_LIST_SWAP_SBO) {
+    if (list->item_size > CX_LINKED_LIST_SWAP_SBO_SIZE) {
         cx_linked_list_node *prev = nleft->prev;
         cx_linked_list_node *next = nright->next;
         cx_linked_list_node *midstart = nleft->next;
@@ -735,13 +756,35 @@
     return node == NULL ? NULL : node->payload;
 }
 
-static ssize_t cx_ll_find(
-        struct cx_list_s const *list,
-        void const *elem
+static ssize_t cx_ll_find_remove(
+        struct cx_list_s *list,
+        void const *elem,
+        bool remove
 ) {
-    return cx_linked_list_find(((cx_linked_list *) list)->begin,
-                               CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
-                               list->cmpfunc, elem);
+    if (remove) {
+        cx_linked_list *ll = ((cx_linked_list *) list);
+        cx_linked_list_node *node;
+        ssize_t index = cx_linked_list_find_node(
+                (void **) &node,
+                ll->begin,
+                CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+                list->cmpfunc, elem
+        );
+        if (node != NULL) {
+            cx_invoke_destructor(list, node->payload);
+            cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
+                                  CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+            list->size--;
+            cxFree(list->allocator, node);
+        }
+        return index;
+    } else {
+        return cx_linked_list_find(
+                ((cx_linked_list *) list)->begin,
+                CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+                list->cmpfunc, elem
+        );
+    }
 }
 
 static void cx_ll_sort(struct cx_list_s *list) {
@@ -894,7 +937,7 @@
         cx_ll_clear,
         cx_ll_swap,
         cx_ll_at,
-        cx_ll_find,
+        cx_ll_find_remove,
         cx_ll_sort,
         cx_ll_compare,
         cx_ll_reverse,
@@ -915,11 +958,12 @@
 
     list->base.cl = &cx_linked_list_class;
     list->base.allocator = allocator;
-    list->base.cmpfunc = comparator;
 
     if (item_size > 0) {
         list->base.item_size = item_size;
+        list->base.cmpfunc = comparator;
     } else {
+        list->base.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator;
         cxListStorePointers((CxList *) list);
     }
 
--- a/ucx/list.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/list.c	Sun Feb 11 22:06:23 2024 +0100
@@ -115,12 +115,13 @@
     return ptr == NULL ? NULL : *ptr;
 }
 
-static ssize_t cx_pl_find(
-        struct cx_list_s const *list,
-        void const *elem
+static ssize_t cx_pl_find_remove(
+        struct cx_list_s *list,
+        void const *elem,
+        bool remove
 ) {
     cx_pl_hack_cmpfunc(list);
-    ssize_t ret = list->climpl->find(list, &elem);
+    ssize_t ret = list->climpl->find_remove(list, &elem, remove);
     cx_pl_unhack_cmpfunc(list);
     return ret;
 }
@@ -171,7 +172,7 @@
         cx_pl_clear,
         cx_pl_swap,
         cx_pl_at,
-        cx_pl_find,
+        cx_pl_find_remove,
         cx_pl_sort,
         cx_pl_compare,
         cx_pl_reverse,
@@ -208,9 +209,10 @@
     return NULL;
 }
 
-static ssize_t cx_emptyl_find(
-        __attribute__((__unused__)) struct cx_list_s const *list,
-        __attribute__((__unused__)) void const *elem
+static ssize_t cx_emptyl_find_remove(
+        __attribute__((__unused__)) struct cx_list_s *list,
+        __attribute__((__unused__)) void const *elem,
+        __attribute__((__unused__)) bool remove
 ) {
     return -1;
 }
@@ -248,7 +250,7 @@
         cx_emptyl_noop,
         NULL,
         cx_emptyl_at,
-        cx_emptyl_find,
+        cx_emptyl_find_remove,
         cx_emptyl_noop,
         cx_emptyl_compare,
         cx_emptyl_noop,
@@ -293,8 +295,8 @@
     ) {
         // lists are definitely different - cannot use internal compare function
         if (list->size == other->size) {
-            CxIterator left = cxListIterator(list);
-            CxIterator right = cxListIterator(other);
+            CxIterator left = list->cl->iterator(list, 0, false);
+            CxIterator right = other->cl->iterator(other, 0, false);
             for (size_t i = 0; i < list->size; i++) {
                 void *leftValue = cxIteratorCurrent(left);
                 void *rightValue = cxIteratorCurrent(right);
--- a/ucx/printf.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/printf.c	Sun Feb 11 22:06:23 2024 +0100
@@ -34,6 +34,7 @@
 #ifndef CX_PRINTF_SBO_SIZE
 #define CX_PRINTF_SBO_SIZE 512
 #endif
+unsigned const cx_printf_sbo_size = CX_PRINTF_SBO_SIZE;
 
 int cx_fprintf(
         void *stream,
@@ -60,17 +61,21 @@
     va_copy(ap2, ap);
     int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap);
     if (ret < 0) {
+        va_end(ap2);
         return ret;
     } else if (ret < CX_PRINTF_SBO_SIZE) {
+        va_end(ap2);
         return (int) wfc(buf, 1, ret, stream);
     } else {
         int len = ret + 1;
         char *newbuf = malloc(len);
         if (!newbuf) {
+            va_end(ap2);
             return -1;
         }
 
         ret = vsnprintf(newbuf, len, fmt, ap2);
+        va_end(ap2);
         if (ret > 0) {
             ret = (int) wfc(newbuf, 1, ret, stream);
         }
@@ -85,9 +90,8 @@
         ...
 ) {
     va_list ap;
-    cxmutstr ret;
     va_start(ap, fmt);
-    ret = cx_vasprintf_a(allocator, fmt, ap);
+    cxmutstr ret = cx_vasprintf_a(allocator, fmt, ap);
     va_end(ap);
     return ret;
 }
@@ -104,7 +108,7 @@
     va_list ap2;
     va_copy(ap2, ap);
     int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap);
-    if (ret > 0 && ret < CX_PRINTF_SBO_SIZE) {
+    if (ret >= 0 && ret < CX_PRINTF_SBO_SIZE) {
         s.ptr = cxMalloc(a, ret + 1);
         if (s.ptr) {
             s.length = (size_t) ret;
@@ -124,6 +128,65 @@
             }
         }
     }
+    va_end(ap2);
     return s;
 }
 
+int cx_sprintf_a(CxAllocator *alloc, char **str, size_t len, const char *fmt, ... ) {
+    va_list ap;
+    va_start(ap, fmt);
+    int ret = cx_vsprintf_a(alloc, str, len, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t len, const char *fmt, va_list ap) {
+    va_list ap2;
+    va_copy(ap2, ap);
+    int ret = vsnprintf(*str, len, fmt, ap);
+    if ((unsigned) ret >= len) {
+        unsigned newlen = ret + 1;
+        char *ptr = cxRealloc(alloc, *str, newlen);
+        if (ptr) {
+            int newret = vsnprintf(ptr, newlen, fmt, ap2);
+            if (newret < 0) {
+                cxFree(alloc, ptr);
+            } else {
+                *str = ptr;
+                ret = newret;
+            }
+        }
+    }
+    va_end(ap2);
+    return ret;
+}
+
+int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t len, char **str, const char *fmt, ... ) {
+    va_list ap;
+    va_start(ap, fmt);
+    int ret = cx_vsprintf_sa(alloc, buf, len, str, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+int cx_vsprintf_sa(CxAllocator *alloc, char *buf, size_t len, char **str, const char *fmt, va_list ap) {
+    va_list ap2;
+    va_copy(ap2, ap);
+    int ret = vsnprintf(buf, len, fmt, ap);
+    *str = buf;
+    if ((unsigned) ret >= len) {
+        unsigned newlen = ret + 1;
+        char *ptr = cxMalloc(alloc, newlen);
+        if (ptr) {
+            int newret = vsnprintf(ptr, newlen, fmt, ap2);
+            if (newret < 0) {
+                cxFree(alloc, ptr);
+            } else {
+                *str = ptr;
+                ret = newret;
+            }
+        }
+    }
+    va_end(ap2);
+    return ret;
+}
--- a/ucx/string.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ucx/string.c	Sun Feb 11 22:06:23 2024 +0100
@@ -236,6 +236,7 @@
 #ifndef CX_STRSTR_SBO_SIZE
 #define CX_STRSTR_SBO_SIZE 512
 #endif
+unsigned const cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE;
 
 cxstring cx_strstr(
         cxstring haystack,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/tree.c	Sun Feb 11 22:06:23 2024 +0100
@@ -0,0 +1,87 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 Mike Becker, 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 "cx/tree.h"
+
+#include <assert.h>
+
+#define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
+#define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
+#define tree_parent(node) CX_TREE_PTR(node, loc_parent)
+#define tree_children(node) CX_TREE_PTR(node, loc_children)
+#define tree_prev(node) CX_TREE_PTR(node, loc_prev)
+#define tree_next(node) CX_TREE_PTR(node, loc_next)
+
+void cx_tree_link(
+        void *restrict parent,
+        void *restrict node,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    void *current_parent = tree_parent(node);
+    if (current_parent == parent) return;
+    if (current_parent != NULL) {
+        cx_tree_unlink(node, loc_parent, loc_children,
+                       loc_prev, loc_next);
+    }
+
+    if (tree_children(parent) == NULL) {
+        tree_children(parent) = node;
+    } else {
+        void *children = tree_children(parent);
+        tree_prev(children) = node;
+        tree_next(node) = children;
+        tree_children(parent) = node;
+    }
+    tree_parent(node) = parent;
+}
+
+void cx_tree_unlink(
+        void *node,
+        ptrdiff_t loc_parent,
+        ptrdiff_t loc_children,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    if (tree_parent(node) == NULL) return;
+
+    void *left = tree_prev(node);
+    void *right = tree_next(node);
+    assert(left == NULL || tree_children(tree_parent(node)) != node);
+    if (left == NULL) {
+        tree_children(tree_parent(node)) = right;
+    } else {
+        tree_next(left) = right;
+    }
+    if (right != NULL) tree_prev(right) = left;
+    tree_parent(node) = NULL;
+    tree_prev(node) = NULL;
+    tree_next(node) = NULL;
+}
--- a/ui/gtk/button.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/button.c	Sun Feb 11 22:06:23 2024 +0100
@@ -35,7 +35,7 @@
 #include "../common/context.h"
 #include "../common/object.h"
 
-UIWIDGET ui_button(UiObject *obj, char *label, ui_callback f, void *data) {
+UIWIDGET ui_button_deprecated(UiObject *obj, char *label, ui_callback f, void *data) {
     GtkWidget *button = gtk_button_new_with_label(label);
     
     if(f) {
@@ -132,7 +132,7 @@
     return button;
 }
 
-UIWIDGET ui_checkbox(UiObject *obj, char *label, UiInteger *value) {
+UIWIDGET ui_checkbox_deprecated(UiObject *obj, char *label, UiInteger *value) {
     UiVar *var = NULL;
     if(value) {
         var = malloc(sizeof(UiVar));
@@ -190,7 +190,7 @@
     return rbutton;
 }
 
-UIWIDGET ui_radiobutton(UiObject *obj, char *label, UiInteger *rgroup) {
+UIWIDGET ui_radiobutton_deprecated(UiObject *obj, char *label, UiInteger *rgroup) {
     UiVar *var = NULL;
     if(rgroup) {
         var = malloc(sizeof(UiVar));
--- a/ui/gtk/container.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/container.c	Sun Feb 11 22:06:23 2024 +0100
@@ -67,22 +67,6 @@
 #endif
 }
 
-/* -------------------- Frame Container (deprecated) -------------------- */
-UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) {
-    UiContainer *ct = cxCalloc(
-            obj->ctx->allocator,
-            1,
-            sizeof(UiContainer));
-    ct->widget = frame;
-    ct->add = ui_frame_container_add;
-    return ct;
-}
-
-void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    gtk_container_add(GTK_CONTAINER(ct->widget), widget);
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
 
 
 /* -------------------- Box Container -------------------- */
@@ -243,13 +227,6 @@
 }
 
 
-UIWIDGET ui_vbox(UiObject *obj) {
-    return ui_vbox_sp(obj, 0, 0);
-}
-
-UIWIDGET ui_hbox(UiObject *obj) {
-    return ui_hbox_sp(obj, 0, 0);
-}
 
 static GtkWidget* box_set_margin(GtkWidget *box, int margin) {
     GtkWidget *ret = box;
@@ -272,11 +249,13 @@
     return ret;
 }
 
-UIWIDGET ui_vbox_sp(UiObject *obj, int margin, int spacing) {
-    UiContainer *ct = uic_get_current_container(obj);
+UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, UiSubContainerType type) {
+    UiObject *current = uic_current_obj(obj);
+    UiContainer *ct = current->container;
+    UI_APPLY_LAYOUT1(current, args);
     
-    GtkWidget *vbox = ui_gtk_vbox_new(spacing);   
-    GtkWidget *widget = margin > 0 ? box_set_margin(vbox, margin) : vbox;
+    GtkWidget *vbox = ui_gtk_vbox_new(args.spacing);   
+    GtkWidget *widget = args.margin > 0 ? box_set_margin(vbox, args.margin) : vbox;
     ct->add(ct, widget, TRUE);
     
     UiObject *newobj = uic_object_new(obj, vbox);
@@ -286,41 +265,33 @@
     return widget;
 }
 
-UIWIDGET ui_hbox_sp(UiObject *obj, int margin, int spacing) {
-    UiContainer *ct = uic_get_current_container(obj);
-    
-    GtkWidget *hbox = ui_gtk_hbox_new(spacing);
-    GtkWidget *widget = margin > 0 ? box_set_margin(hbox, margin) : hbox;
-    ct->add(ct, widget, TRUE);
-    
-    UiObject *newobj = uic_object_new(obj, hbox);
-    newobj->container = ui_box_container(obj, hbox);
-    uic_obj_add(obj, newobj);
-    
-    return widget;
+UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args) {
+    return ui_box_create(obj, args, UI_CONTAINER_VBOX);
 }
 
-UIWIDGET ui_grid(UiObject *obj) {
-    return ui_grid_sp(obj, 0, 0, 0);
+UIEXPORT UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args) {
+    return ui_box_create(obj, args, UI_CONTAINER_HBOX);
 }
 
-UIWIDGET ui_grid_sp(UiObject *obj, int margin, int columnspacing, int rowspacing) {
+
+
+UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) {
     UiContainer *ct = uic_get_current_container(obj);
     GtkWidget *widget;
     
 #ifdef UI_GTK3
     GtkWidget *grid = gtk_grid_new();
-    gtk_grid_set_column_spacing(GTK_GRID(grid), columnspacing);
-    gtk_grid_set_row_spacing(GTK_GRID(grid), rowspacing);
+    gtk_grid_set_column_spacing(GTK_GRID(grid), args.columnspacing);
+    gtk_grid_set_row_spacing(GTK_GRID(grid), args.rowspacing);
 #if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 12
-    gtk_widget_set_margin_start(grid, margin);
-    gtk_widget_set_margin_end(grid, margin);
+    gtk_widget_set_margin_start(grid, args.margin);
+    gtk_widget_set_margin_end(grid, args.margin);
 #else
     gtk_widget_set_margin_left(grid, margin);
     gtk_widget_set_margin_right(grid, margin);
 #endif
-    gtk_widget_set_margin_top(grid, margin);
-    gtk_widget_set_margin_bottom(grid, margin);
+    gtk_widget_set_margin_top(grid, args.margin);
+    gtk_widget_set_margin_bottom(grid, args.margin);
     
     widget = grid;
 #elif defined(UI_GTK2)
@@ -347,38 +318,6 @@
     return widget;
 }
 
-UIWIDGET ui_scrolledwindow(UiObject *obj) {
-    UiContainer *ct = uic_get_current_container(obj);
-    GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
-    ct->add(ct, sw, TRUE);
-    
-    UiObject *newobj = uic_object_new(obj, sw);
-    newobj->container = ui_scrolledwindow_container(obj, sw);
-    uic_obj_add(obj, newobj);
-    
-    return sw;
-}
-
-UIWIDGET ui_tabview(UiObject *obj) {
-    GtkWidget *tabview = gtk_notebook_new();
-    gtk_notebook_set_show_border(GTK_NOTEBOOK(tabview), FALSE);
-    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(tabview), FALSE);
-    
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, tabview, TRUE);
-    
-    UiObject *tabviewobj = uic_object_new(obj, tabview);
-    tabviewobj->container = ui_tabview_container(obj, tabview);
-    uic_obj_add(obj, tabviewobj);
-    
-    return tabview;
-}
-
-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) {
     gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab);
@@ -401,192 +340,9 @@
     return NULL;
 }
 
-UIWIDGET ui_splitpane(UiObject *obj, int max, UiOrientation orientation) {
-    GtkWidget *paned = create_paned(orientation);
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, paned, TRUE);
-    
-    if(max <= 0) max = INT_MAX;
-    
-    UiPanedContainer *pctn = cxCalloc(
-            obj->ctx->allocator,
-            1,
-            sizeof(UiPanedContainer));
-    pctn->container.widget = paned;
-    pctn->container.add = ui_paned_container_add;
-    pctn->current_pane = paned;
-    pctn->orientation = orientation;
-    pctn->max = max;
-    pctn->cur = 0;
-    
-    UiObject *pobj = uic_object_new(obj, paned);
-    pobj->container = (UiContainer*)pctn;
-    
-    uic_obj_add(obj, pobj);
-    
-    return paned;
-}
-
-UIWIDGET ui_hsplitpane(UiObject *obj, int max) {
-    return ui_splitpane(obj, max, UI_HORIZONTAL);
-}
-
-UIWIDGET ui_vsplitpane(UiObject *obj, int max) {
-    return ui_splitpane(obj, max, UI_VERTICAL);
-}
-
-void ui_paned_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    UiPanedContainer *pctn = (UiPanedContainer*)ct;
-    
-    gboolean resize = (ct->layout.hexpand || ct->layout.vexpand) ? TRUE : FALSE;
-    int width = ct->layout.width;
-    ui_reset_layout(ct->layout);
-    
-    if(pctn->cur == 0) {
-        gtk_paned_pack1(GTK_PANED(pctn->current_pane), widget, resize, resize);
-    } else if(pctn->cur < pctn->max-1) {
-        GtkWidget *nextPane = create_paned(pctn->orientation);
-        gtk_paned_pack2(GTK_PANED(pctn->current_pane), nextPane, TRUE, TRUE);
-        gtk_paned_pack1(GTK_PANED(nextPane), widget, resize, resize);
-        pctn->current_pane = nextPane;
-    } else if(pctn->cur == pctn->max-1) {
-        gtk_paned_pack2(GTK_PANED(pctn->current_pane), widget, resize, resize);
-        width = 0; // disable potential call of gtk_paned_set_position
-    } else {
-        fprintf(stderr, "Splitpane max reached: %d\n", pctn->max);
-        return;
-    }
-    
-    if(width > 0) {
-        gtk_paned_set_position(GTK_PANED(pctn->current_pane), width);
-    }
-    
-    pctn->cur++;
-}
 
 
-/* -------------------- Sidebar (deprecated) -------------------- */
-UIWIDGET ui_sidebar(UiObject *obj) {
-#ifdef UI_GTK3
-    GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
-#else
-    GtkWidget *paned = gtk_hpaned_new();
-#endif
-    gtk_paned_set_position(GTK_PANED(paned), 200);
-    
-    GtkWidget *sidebar = ui_gtk_vbox_new(0);
-    gtk_paned_pack1(GTK_PANED(paned), sidebar, TRUE, FALSE);
-    
-    UiObject *left = uic_object_new(obj, sidebar);
-    UiContainer *ct1 = ui_box_container(obj, sidebar);
-    left->container = ct1;
-    
-    UiObject *right = uic_object_new(obj, sidebar);
-    UiContainer *ct2 = cxCalloc(
-            obj->ctx->allocator,
-            1,
-            sizeof(UiContainer));
-    ct2->widget = paned;
-    ct2->add = ui_split_container_add2;
-    right->container = ct2;
-    
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, paned, TRUE);
-    
-    uic_obj_add(obj, right);
-    uic_obj_add(obj, left);
-    
-    return sidebar;
-}
 
-void ui_split_container_add1(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    // TODO: remove
-    gtk_paned_pack1(GTK_PANED(ct->widget), widget, TRUE, FALSE);
-    
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
-void ui_split_container_add2(UiContainer *ct, GtkWidget *widget, UiBool fill) {
-    gtk_paned_pack2(GTK_PANED(ct->widget), widget, TRUE, FALSE);
-    
-    ui_reset_layout(ct->layout);
-    ct->current = widget;
-}
-
-
-/* -------------------- Document Tabview -------------------- */
-static void page_change(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer data) {
-    GQuark q = g_quark_from_static_string("ui.tab.object");
-    UiObject *tab = g_object_get_qdata(G_OBJECT(page), q);
-    if(!tab) {
-        return;
-    }
-    
-    //printf("page_change: %d\n", page_num);
-    UiContext *ctx = tab->ctx;
-    uic_context_detach_all(ctx->parent); // TODO: fix?
-    ctx->parent->attach_document(ctx->parent, ctx->document);
-}
-
-UiTabbedPane* ui_tabbed_document_view(UiObject *obj) {
-    GtkWidget *tabview = gtk_notebook_new();
-    gtk_notebook_set_show_border(GTK_NOTEBOOK(tabview), FALSE);
-    
-    g_signal_connect(
-                tabview,
-                "switch-page",
-                G_CALLBACK(page_change),
-                NULL);
-    
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, tabview, TRUE);
-    
-    UiTabbedPane *tabbedpane = ui_malloc(obj->ctx, sizeof(UiTabbedPane));
-    tabbedpane->ctx = uic_current_obj(obj)->ctx;
-    tabbedpane->widget = tabview;
-    tabbedpane->document = NULL;
-    
-    return tabbedpane;
-}
-
-UiObject* ui_document_tab(UiTabbedPane *view) {
-    GtkWidget *frame = gtk_frame_new(NULL);
-    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
-    // TODO: label
-    gtk_notebook_append_page(GTK_NOTEBOOK(view->widget), frame, NULL);
-    
-    UiObject *tab = ui_malloc(view->ctx, sizeof(UiObject));
-    tab->widget = NULL; // initialization for uic_context()
-    tab->ctx = uic_context(tab, view->ctx->allocator);
-    tab->ctx->parent = view->ctx;
-    tab->ctx->attach_document = uic_context_attach_document;
-    tab->ctx->detach_document2 = uic_context_detach_document2;
-    tab->widget = frame;
-    tab->window = view->ctx->obj->window;
-    tab->container = ui_frame_container(tab, frame);
-    tab->next = NULL;
-    
-    GQuark q = g_quark_from_static_string("ui.tab.object");
-    g_object_set_qdata(G_OBJECT(frame), q, tab);
-    
-    return tab;
-}
-
-void ui_tab_set_document(UiContext *ctx, void *document) {
-    // TODO: remove?
-    if(ctx->parent->document) {
-        //ctx->parent->detach_document(ctx->parent, ctx->parent->document);
-    }
-    //uic_context_set_document(ctx, document);
-    //uic_context_set_document(ctx->parent, document);
-    //ctx->parent->document = document;
-}
-
-void ui_tab_detach_document(UiContext *ctx) {
-    // TODO: remove?
-    //uic_context_detach_document(ctx->parent);
-}
 
 
 /*
@@ -611,16 +367,21 @@
     ct->layout.vexpand = expand;
 }
 
-void ui_layout_width(UiObject *obj, int width) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.width = width;
-}
-
 void ui_layout_gridwidth(UiObject *obj, int width) {
     UiContainer *ct = uic_get_current_container(obj);
     ct->layout.gridwidth = width;
 }
 
+void ui_layout_colspan(UiObject* obj, int cols) {
+    UiContainer* ct = uic_get_current_container(obj);
+    ct->layout.colspan = cols;
+}
+
+void ui_layout_rowspan(UiObject* obj, int rows) {
+    UiContainer* ct = uic_get_current_container(obj);
+    ct->layout.rowspan = rows;
+}
+
 void ui_newline(UiObject *obj) {
     UiContainer *ct = uic_get_current_container(obj);
     ct->layout.newline = TRUE;
--- a/ui/gtk/container.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/container.h	Sun Feb 11 22:06:23 2024 +0100
@@ -62,6 +62,8 @@
     UiBool       vexpand;
     int          width;
     int          gridwidth;
+    int          colspan;
+    int          rowspan;
 };
 
 struct UiContainer {
@@ -108,6 +110,8 @@
 UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame);
 void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill);
 
+UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, UiSubContainerType type);
+
 UiContainer* ui_box_container(UiObject *obj, GtkWidget *box);
 void ui_box_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill);
 
--- a/ui/gtk/dnd.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/dnd.c	Sun Feb 11 22:06:23 2024 +0100
@@ -55,6 +55,7 @@
 #define gtk_selection_data_get_uris selection_data_get_uris
 #endif
 
+/*
 void ui_selection_settext(UiSelection *sel, char *str, int len) {
     // TODO: handle error?
     gtk_selection_data_set_text(sel->data, str, len);
@@ -99,3 +100,4 @@
     }
     return NULL;
 }
+*/
--- a/ui/gtk/image.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/image.c	Sun Feb 11 22:06:23 2024 +0100
@@ -75,9 +75,11 @@
     return NULL;
 }
 
+/*
 UiIcon* ui_icon(const char *name, int size) {
     return get_icon(name, size, ui_get_scalefactor());
 }
+*/
 
 UiIcon* ui_icon_unscaled(const char *name, int size) {
     return get_icon(name, size, 1);
@@ -99,6 +101,7 @@
     return NULL;
 }
 
+/*
 UiImage* ui_image(const char *filename) {
     return ui_named_image(filename, NULL);
 }
@@ -129,6 +132,7 @@
     }
     return img;
 }
+*/
 
 void ui_free_image(UiImage *img) {
     g_object_unref(img->pixbuf);
--- a/ui/gtk/menu.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/menu.c	Sun Feb 11 22:06:23 2024 +0100
@@ -76,6 +76,12 @@
 }
 
 void add_menu_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj) {
+    // TODO
+}
+
+#ifdef DEPRECATED_TOOLKIT
+
+void add_menu_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj) {
     UiMenu *menu = (UiMenu*)item;
     
     GtkWidget *menu_widget = gtk_menu_new();
@@ -472,3 +478,5 @@
         cxListDestroy(groups);
     }
 }
+
+#endif
--- a/ui/gtk/model.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/model.c	Sun Feb 11 22:06:23 2024 +0100
@@ -436,6 +436,7 @@
 {
     //printf("drag received\n");
     UiListModel *model = UI_LIST_MODEL(drag_dest);
+    /*
     if(model->model->drop) {
         gint *indices = gtk_tree_path_get_indices(dest_path);
         gint row = indices[0];
@@ -449,6 +450,7 @@
         s.data = selection_data;
         model->model->drop(&e, &s, model->var->value, row);
     }
+    */
     return TRUE;
 }
 
@@ -459,6 +461,7 @@
 {
     //printf("row_drop_possible\n");
     UiListModel *model = UI_LIST_MODEL(drag_dest);
+    /*
     if(model->model->candrop) {
         gint *indices = gtk_tree_path_get_indices(dest_path);
         gint row = indices[0];
@@ -472,6 +475,7 @@
         s.data = selection_data;
         return model->model->candrop(&e, &s, model->var->value, row);
     }
+    */
     return TRUE;
 }
 
@@ -481,6 +485,7 @@
 {
     //printf("row_draggable\n");
     UiListModel *model = UI_LIST_MODEL(drag_source);
+    /*
     if(model->model->candrag) {
         gint *indices = gtk_tree_path_get_indices(path);
         gint row = indices[0];
@@ -492,6 +497,7 @@
         e.intval = 0;
         return model->model->candrag(&e, model->var->value, row);
     }
+    */
     return TRUE;
 }
 
@@ -502,6 +508,7 @@
 {
     //printf("drag_data_get\n");
     UiListModel *model = UI_LIST_MODEL(drag_source);
+    /*
     if(model->model->data_get) {
         gint *indices = gtk_tree_path_get_indices(path);
         gint row = indices[0];
@@ -515,6 +522,7 @@
         s.data = selection_data;
         model->model->data_get(&e, &s, model->var->value, row);
     }
+    */
     return TRUE;
 }
 
@@ -524,6 +532,7 @@
 {
     //printf("drag_data_delete\n");
     UiListModel *model = UI_LIST_MODEL(drag_source);
+    /*
     if(model->model->data_get) {
         gint *indices = gtk_tree_path_get_indices(path);
         gint row = indices[0];
@@ -535,5 +544,6 @@
         e.intval = 0;
         model->model->data_delete(&e, model->var->value, row);
     }
+    */
     return TRUE;
 }
--- a/ui/gtk/text.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/text.c	Sun Feb 11 22:06:23 2024 +0100
@@ -193,7 +193,7 @@
     return str;
 }
 
-void ui_textarea_set(UiText *text, char *str) {
+void ui_textarea_set(UiText *text, const char *str) {
     gtk_text_buffer_set_text((GtkTextBuffer*)text->obj, str, -1);
     if(text->value.ptr) {
         text->value.free(text->value.ptr);
@@ -632,7 +632,7 @@
     }
 }
 
-UIWIDGET ui_textfield(UiObject *obj, UiString *value) {
+UIWIDGET ui_textfield_deprecated(UiObject *obj, UiString *value) {
     return create_textfield(obj, 0, FALSE, FALSE, value);
 }
 
@@ -648,7 +648,7 @@
     return create_textfield_nv(obj, width, FALSE, FALSE, varname);
 }
 
-UIWIDGET ui_frameless_textfield(UiObject *obj, UiString *value) {
+UIWIDGET ui_frameless_textfield_deprecated(UiObject *obj, UiString *value) {
     return create_textfield(obj, 0, TRUE, FALSE, value);
 }
 
@@ -656,7 +656,7 @@
     return create_textfield_nv(obj, 0, TRUE, FALSE, varname);
 }
 
-UIWIDGET ui_passwordfield(UiObject *obj, UiString *value) {
+UIWIDGET ui_passwordfield_deprecated(UiObject *obj, UiString *value) {
     return create_textfield(obj, 0, FALSE, TRUE, value);
 }
 
@@ -681,7 +681,7 @@
     return str->value.ptr;
 }
 
-void ui_textfield_set(UiString *str, char *value) {
+void ui_textfield_set(UiString *str, const char *value) {
     gtk_entry_set_text(str->obj, value);
     if(str->value.ptr) {
         str->value.free(str->value.ptr);
--- a/ui/gtk/text.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/text.h	Sun Feb 11 22:06:23 2024 +0100
@@ -76,7 +76,7 @@
 void ui_textarea_destroy(GtkWidget *object, UiTextArea *textarea);
 
 char* ui_textarea_get(UiText *text);
-void ui_textarea_set(UiText *text, char *str);
+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);
@@ -108,7 +108,7 @@
 void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield);
 
 char* ui_textfield_get(UiString *str);
-void ui_textfield_set(UiString *str, char *value);
+void ui_textfield_set(UiString *str, const char *value);
 
 #ifdef	__cplusplus
 }
--- a/ui/gtk/toolbar.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/toolbar.c	Sun Feb 11 22:06:23 2024 +0100
@@ -34,7 +34,7 @@
 #include "button.h"
 #include "image.h"
 #include "tree.h"
-#include <cx/basic_mempool.h>
+#include <cx/mempool.h>
 #include <cx/hash_map.h>
 #include <cx/linked_list.h>
 #include <cx/array_list.h>
@@ -221,7 +221,7 @@
 }
 
 
-void ui_toolbar_add_default(char *name) {
+void ui_toolbar_add_default(const char *name, enum UiToolbarPos pos) {
     char *s = strdup(name);
     cxListAdd(defaults, s);
 }
--- a/ui/gtk/toolkit.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/toolkit.c	Sun Feb 11 22:06:23 2024 +0100
@@ -48,7 +48,7 @@
 static GtkApplication *app;
 #endif
 
-static char *application_name;
+static const char *application_name;
 
 static ui_callback   startup_func;
 static void          *startup_data;
@@ -64,7 +64,7 @@
 
 static int scale_factor = 1;
 
-void ui_init(char *appname, int argc, char **argv) {
+UIEXPORT void ui_init(const char *appname, int argc, char **argv) {
     uic_init_global_context();
     
     gtk_init(&argc, &argv);
@@ -86,7 +86,7 @@
 #endif
 }
 
-char* ui_appname() {
+const char* ui_appname() {
     return application_name;
 }
 
--- a/ui/gtk/tree.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/tree.c	Sun Feb 11 22:06:23 2024 +0100
@@ -43,9 +43,7 @@
 }
 
 
-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) {
     // create treeview
@@ -119,7 +117,7 @@
     return scroll_area;
 }
 
-UIWIDGET ui_listview(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
+UIWIDGET ui_listview_deprecated(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;
@@ -270,7 +268,7 @@
     return scroll_area;
 }
 
-UIWIDGET ui_table(UiObject *obj, UiList *list, UiModel *model, UiListCallbacks cb) {
+UIWIDGET ui_table_deprecated(UiObject *obj, UiList *list, UiModel *model, UiListCallbacks cb) {
     UiVar *var = malloc(sizeof(UiVar));
     var->value = list;
     var->type = UI_VAR_SPECIAL;
@@ -329,10 +327,14 @@
     int nelm;
     char **targets = targets2array(target0, ap, &nelm);
     va_end(ap);
-    ui_table_dragsource_a(tablewidget, actions, targets, nelm);
+    
+    // disabled
+    //ui_table_dragsource_a(tablewidget, actions, targets, nelm);
+    
     free(targets);
 }
 
+/*
 void ui_table_dragsource_a(UIWIDGET tablewidget, int actions, char **targets, int nelm) {
     GtkTargetEntry* t = targetstr2gtktargets(targets, nelm);
     gtk_tree_view_enable_model_drag_source(
@@ -344,6 +346,7 @@
     free(t);
 }
 
+
 void ui_table_dragdest(UIWIDGET tablewidget, int actions, char *target0, ...) {
     va_list ap;
     va_start(ap, target0);
@@ -363,7 +366,8 @@
             GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK);
     free(t);
 }
-
+*/
+ 
 void ui_listview_update(UiList *list, int i) {
     UiListView *view = list->obj;
     UiListModel *model = ui_list_model_new(view->obj, view->var, view->model);
@@ -464,11 +468,7 @@
 
 /* --------------------------- 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) {
+UIWIDGET ui_combobox_deprecated(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;
--- a/ui/gtk/window.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/gtk/window.c	Sun Feb 11 22:06:23 2024 +0100
@@ -34,7 +34,7 @@
 #include "../ui/properties.h"
 #include "../common/context.h"
 
-#include <cx/basic_mempool.h>
+#include <cx/mempool.h>
 
 #include "menu.h"
 #include "toolbar.h"
@@ -67,7 +67,7 @@
 #endif
 }
 
-static UiObject* create_window(char *title, void *window_data, UiBool simple) {
+static UiObject* create_window(const char *title, void *window_data, UiBool simple) {
     CxMempool *mp = cxBasicMempoolCreate(256);
     UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); 
     
@@ -78,7 +78,7 @@
 #endif
     
     
-    obj->ctx = uic_context(obj, mp->allocator);
+    obj->ctx = uic_context(obj, mp);
     obj->window = window_data;
     
     if(title != NULL) {
@@ -138,11 +138,11 @@
 }
 
 
-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_simplewindow(const char *title, void *window_data) {
     return create_window(title, window_data, TRUE);
 }
 
@@ -179,11 +179,4 @@
     }
 }
 
-char* ui_openfiledialog(UiObject *obj) {
-    return ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_OPEN);
-}
 
-char* ui_savefiledialog(UiObject *obj) {
-    return ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_SAVE);
-}
-
--- a/ui/motif/container.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/motif/container.c	Sun Feb 11 22:06:23 2024 +0100
@@ -674,7 +674,7 @@
     
     UiObject *content = ui_malloc(view->ctx, sizeof(UiObject));
     content->widget = NULL; // initialization for uic_context()
-    content->ctx = uic_context(content, view->ctx->allocator);
+    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;
--- a/ui/motif/text.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/motif/text.c	Sun Feb 11 22:06:23 2024 +0100
@@ -119,7 +119,7 @@
     return str;
 }
 
-void ui_textarea_set(UiText *text, char *str) {
+void ui_textarea_set(UiText *text, const char *str) {
     XmTextSetString(text->obj, str);
     if(text->value.ptr) {
         text->value.free(text->value.ptr);
--- a/ui/motif/text.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/motif/text.h	Sun Feb 11 22:06:23 2024 +0100
@@ -64,7 +64,7 @@
 } UiTextArea;
     
 char* ui_textarea_get(UiText *text);
-void ui_textarea_set(UiText *text, char *str);
+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);
--- a/ui/motif/window.c	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/motif/window.c	Sun Feb 11 22:06:23 2024 +0100
@@ -36,7 +36,7 @@
 #include "../ui/window.h"
 #include "../common/context.h"
 
-#include <cx/basic_mempool.h>
+#include <cx/mempool.h>
 
 static int nwindows = 0;
 
@@ -67,7 +67,7 @@
     CxMempool *mp = cxBasicMempoolCreate(256);
     const CxAllocator *a = mp->allocator;
     UiObject *obj = cxCalloc(a, 1, sizeof(UiObject));
-    obj->ctx = uic_context(obj, a);
+    obj->ctx = uic_context(obj, mp);
     obj->window = window_data;
     
     Arg args[16];
--- a/ui/ui/toolkit.h	Sun Feb 11 15:44:33 2024 +0100
+++ b/ui/ui/toolkit.h	Sun Feb 11 22:06:23 2024 +0100
@@ -297,7 +297,7 @@
 };
 
 struct UiText {
-    void  (*set)(UiText*, char*);
+    void  (*set)(UiText*, const char*);
     char* (*get)(UiText*);
     char* (*getsubstr)(UiText*, int, int); /* text, begin, end */
     void  (*insert)(UiText*, int, char*);

mercurial