]> uap-core.de Git - uwplayer.git/commitdiff
use a custom widget to create the player widget
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Tue, 18 Nov 2025 16:44:04 +0000 (17:44 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Tue, 18 Nov 2025 16:44:04 +0000 (17:44 +0100)
44 files changed:
application/window.c
ucx/array_list.c
ucx/cx/array_list.h
ucx/cx/collection.h
ucx/cx/hash_key.h
ucx/cx/list.h
ucx/cx/map.h
ucx/cx/string.h
ucx/hash_key.c
ucx/hash_map.c
ucx/kv_list.c
ucx/linked_list.c
ucx/list.c
ucx/map.c
ucx/mempool.c
ui/common/context.c
ui/common/context.h
ui/common/menu.c
ui/common/menu.h
ui/common/types.c
ui/common/types.h
ui/common/wrapper.c
ui/common/wrapper.h
ui/gtk/list.c
ui/gtk/list.h
ui/gtk/menu.c
ui/gtk/text.c
ui/motif/Grid.c
ui/motif/Grid.h
ui/motif/button.c
ui/motif/container.c
ui/motif/container.h
ui/motif/entry.c [new file with mode: 0644]
ui/motif/entry.h [new file with mode: 0644]
ui/motif/label.c
ui/motif/list.c
ui/motif/list.h
ui/motif/menu.c
ui/motif/objs.mk
ui/motif/text.c
ui/motif/text.h
ui/motif/toolkit.c
ui/motif/toolkit.h
ui/ui/toolkit.h

index 8e82cb82011c2269a00854fe2f361773cb807789..eb83472355ca19155908abbc7593241507522704 100644 (file)
@@ -266,6 +266,19 @@ static UIWIDGET create_motif_listview(UiObject *obj, UiWidgetArgs *args, void *u
     
     return w;
 }
+
+static UIWIDGET create_motif_player(UiObject *obj, UiWidgetArgs *args, void *userdata, Widget parent, Arg *a, int n) {
+    MainWindow *window = userdata;
+    XtSetArg(a[n], XmNbackground, BlackPixelOfScreen(XtScreen(parent))); n++;
+    Widget player_widget = XmCreateDrawingArea(parent, "player", a, n);
+    XtAddCallback(player_widget, XmNinputCallback, playerWidgetInputCB, window);
+    XmProcessTraversal(player_widget, XmTRAVERSE_CURRENT);
+    XtAddEventHandler(player_widget,
+            PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
+            EnterWindowMask | KeyPressMask | KeyReleaseMask | LeaveWindowMask,
+            FALSE, playerEH, window);
+    return player_widget;
+}
 #endif
 
 MainWindow* WindowCreate(Display *display) {
@@ -300,7 +313,7 @@ MainWindow* WindowCreate(Display *display) {
     }
     */
     ui_hbox(obj, .fill = TRUE) {
-        window->player_widget = ui_drawingarea(obj, .width = 400, .height = 200);
+        window->player_widget = ui_customwidget(obj, create_motif_player, window, .fill = TRUE);
         
 #ifndef UI_MOTIF
         window->listview = ui_listview(obj, .varname = "playlist", .width = 300);
index 03c0bfe7f03ccddc02b6ee6633eb67be1e9d5f45..964873fe78f29c9d64930922df2c128ad5dafc59 100644 (file)
@@ -102,13 +102,32 @@ struct cx_array_reallocator_s cx_array_reallocator(
 
 // LOW LEVEL ARRAY LIST FUNCTIONS
 
-static size_t cx_array_align_capacity(
-        size_t cap,
-        size_t alignment,
-        size_t max
+/**
+ * Intelligently calculates a new capacity, reserving some more
+ * elements than required to prevent too many allocations.
+ *
+ * @param current_capacity the current capacity of the array
+ * @param needed_capacity the required capacity of the array
+ * @param maximum_capacity the maximum capacity (given by the data type)
+ * @return the new capacity
+ */
+static size_t cx_array_grow_capacity(
+    size_t current_capacity,
+    size_t needed_capacity,
+    size_t maximum_capacity
 ) {
-    if (cap > max - alignment) {
-        return cap;
+    if (current_capacity >= needed_capacity) {
+        return current_capacity;
+    }
+    size_t cap = needed_capacity;
+    size_t alignment;
+    if (cap < 128) alignment = 16;
+    else if (cap < 1024) alignment = 64;
+    else if (cap < 8192) alignment = 512;
+    else alignment = 1024;
+
+    if (cap - 1 > maximum_capacity - alignment) {
+        return maximum_capacity;
     } else {
         return cap - (cap % alignment) + alignment;
     }
@@ -176,10 +195,6 @@ int cx_array_reserve(
 
     // reallocate if possible
     if (newcap > oldcap) {
-        // calculate new capacity (next number divisible by 16)
-        newcap = cx_array_align_capacity(newcap, 16, max_size);
-
-        // perform reallocation
         void *newmem = reallocator->realloc(
                 *array, oldcap, newcap, elem_size, reallocator
         );
@@ -269,22 +284,18 @@ int cx_array_copy(
     }
 
     // check if resize is required
-    size_t minsize = index + elem_count;
-    size_t newsize = oldsize < minsize ? minsize : oldsize;
+    const size_t minsize = index + elem_count;
+    const size_t newsize = oldsize < minsize ? minsize : oldsize;
 
-    // reallocate if possible
-    size_t newcap = oldcap;
-    if (newsize > oldcap) {
-        // check, if we need to repair the src pointer
+    // reallocate if necessary
+    const size_t newcap = cx_array_grow_capacity(oldcap, newsize, max_size);
+    if (newcap > oldcap) {
+        // check if we need to repair the src pointer
         uintptr_t targetaddr = (uintptr_t) *target;
         uintptr_t srcaddr = (uintptr_t) src;
         bool repairsrc = targetaddr <= srcaddr
                          && srcaddr < targetaddr + oldcap * elem_size;
 
-        // calculate new capacity (next number divisible by 16)
-        newcap = cx_array_align_capacity(newsize, 16, max_size);
-        assert(newcap > newsize);
-
         // perform reallocation
         void *newmem = reallocator->realloc(
                 *target, oldcap, newcap, elem_size, reallocator
@@ -367,16 +378,16 @@ static int cx_array_insert_sorted_impl(
     }
 
     // store some counts
-    size_t old_size = *size;
-    size_t old_capacity = *capacity;
+    const size_t old_size = *size;
+    const size_t old_capacity = *capacity;
     // the necessary capacity is the worst case assumption, including duplicates
-    size_t needed_capacity = old_size + elem_count;
+    const size_t needed_capacity = cx_array_grow_capacity(old_capacity,
+        old_size + elem_count, SIZE_MAX);
 
     // if we need more than we have, try a reallocation
     if (needed_capacity > old_capacity) {
-        size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX);
         void *new_mem = reallocator->realloc(
-                *target, old_capacity, new_capacity, elem_size, reallocator
+                *target, old_capacity, needed_capacity, elem_size, reallocator
         );
         if (new_mem == NULL) {
             // give it up right away, there is no contract
@@ -384,7 +395,7 @@ static int cx_array_insert_sorted_impl(
             return 1;  // LCOV_EXCL_LINE
         }
         *target = new_mem;
-        *capacity = new_capacity;
+        *capacity = needed_capacity;
     }
 
     // now we have guaranteed that we can insert everything
@@ -781,8 +792,8 @@ static size_t cx_arl_insert_array(
 
     // guarantee enough capacity
     if (arl->capacity < list->collection.size + n) {
-        size_t new_capacity = list->collection.size + n;
-        new_capacity = new_capacity - (new_capacity % 16) + 16;
+        const size_t new_capacity = cx_array_grow_capacity(arl->capacity,
+            list->collection.size + n, SIZE_MAX);
         if (cxReallocateArray(
                 list->collection.allocator,
                 &arl->data, new_capacity,
@@ -1129,6 +1140,14 @@ static void cx_arl_iter_prev(void *it) {
     }
 }
 
+static int cx_arl_change_capacity(
+        struct cx_list_s *list,
+        size_t new_capacity
+) {
+    cx_array_list *arl = (cx_array_list *)list;
+    return cxReallocateArray(list->collection.allocator,
+        &arl->data, new_capacity, list->collection.elem_size);
+}
 
 static struct cx_iterator_s cx_arl_iterator(
         const struct cx_list_s *list,
@@ -1166,6 +1185,7 @@ static cx_list_class cx_array_list_class = {
         cx_arl_sort,
         cx_arl_compare,
         cx_arl_reverse,
+        cx_arl_change_capacity,
         cx_arl_iterator,
 };
 
index 97855dd7ecf31a363df436d047f27e6a676032da..88f9542ec74a2984ee3c4fa0a97d4ea0e28cab0a 100644 (file)
@@ -245,6 +245,9 @@ CX_EXPORT CxArrayReallocator cx_array_reallocator(
  * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
  * architecture. If set to zero, the native word width is used.
  *
+ * @note This function will reserve the minimum required capacity to hold
+ * the additional elements and does not perform an overallocation.
+ *
  * @param array a pointer to the target array
  * @param size a pointer to the size of the array
  * @param capacity a pointer to the capacity of the array
@@ -280,6 +283,9 @@ CX_EXPORT int cx_array_reserve(void **array, void *size, void *capacity,
  * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
  * architecture. If set to zero, the native word width is used.
  *
+ * @note When this function does reallocate the array, it may allocate more
+ * space than required to avoid further allocations in the near future.
+ *
  * @param target a pointer to the target array
  * @param size a pointer to the size of the target array
  * @param capacity a pointer to the capacity of the target array
@@ -293,6 +299,7 @@ CX_EXPORT int cx_array_reserve(void **array, void *size, void *capacity,
  * @retval zero success
  * @retval non-zero failure
  * @see cx_array_reallocator()
+ * @see cx_array_reserve()
  */
 cx_attr_nonnull_arg(1, 2, 3, 6)
 CX_EXPORT int cx_array_copy(void **target, void *size, void *capacity, unsigned width,
index 8e900d3de306917ff0ac4a21523a6745509196de..c7c7c92eb2ee3b5c0d1310a6191e781ee7d4943d 100644 (file)
@@ -153,6 +153,14 @@ struct cx_collection_s {
  */
 #define cxCollectionSorted(c) ((c)->collection.sorted || (c)->collection.size == 0)
 
+/**
+ * Sets the compare function for a collection.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param func (@c cx_compare_func) the compare function that shall be used by @c c
+ */
+#define cxCollectionCompareFunc(c, func) (c)->collection.cmpfunc = (func)
+
 /**
  * Sets a simple destructor function for this collection.
  *
index c501e9c164bafa1599db11b2ac578fba3a43791a..23145bbb97356fff3b2dd1a338f0feb4ba08491a 100644 (file)
@@ -227,12 +227,14 @@ CX_INLINE CxHashKey cx_hash_key_identity(CxHashKey key) {
 /**
  * Compare function for hash keys.
  *
- * @param left the first key
- * @param right the second key
+ * The pointers are untyped to be compatible with the cx_compare_func signature.
+ *
+ * @param left (@c CxHashKey*) the first key
+ * @param right (@c CxHashKey*) the second key
  * @return zero when the keys equal, non-zero when they differ
  */
 cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right);
+CX_EXPORT int cx_hash_key_cmp(const void *left, const void *right);
 
 #ifdef __cplusplus
 } // extern "C"
index 47222610bc9789887513671cb9946bea2b284a87..22681d62acf934e9d6443164adf56afd0780a905 100644 (file)
@@ -169,6 +169,12 @@ struct cx_list_class_s {
      */
     void (*reverse)(struct cx_list_s *list);
 
+    /**
+     * Optional member function for changing the capacity.
+     * If the list does not support overallocation, this can be set to @c NULL.
+     */
+    int (*change_capacity)(struct cx_list_s *list, size_t new_capacity);
+
     /**
      * Member function for returning an iterator pointing to the specified index.
      */
@@ -961,6 +967,226 @@ CX_EXPORT int cxListCompare(const CxList *list, const CxList *other);
 CX_EXPORT void cxListFree(CxList *list);
 
 
+/**
+ * Performs a deep clone of one list into another.
+ *
+ * If the destination list already contains elements, the cloned elements
+ * are appended to that list.
+ *
+ * @attention If the cloned elements need to be destroyed by a destructor
+ * function, you must make sure that the destination list also uses this
+ * destructor function.
+ *
+ * @param dst the destination list
+ * @param src the source list
+ * @param clone_func the clone function for the elements
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when all elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListCloneSimple()
+ */
+cx_attr_nonnull_arg(1, 2, 3)
+CX_EXPORT int cxListClone(CxList *dst, const CxList *src,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones elements from a list only if they are not present in another list.
+ *
+ * If the @p minuend does not contain duplicates, this is equivalent to adding
+ * the set difference to the destination list.
+ *
+ * This function is optimized for the case when both the @p minuend and the
+ * @p subtrahend are sorted.
+ *
+ * @param dst the destination list
+ * @param minuend the list to subtract elements from
+ * @param subtrahend the elements that shall be subtracted
+ * @param clone_func the clone function for the elements
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListDifferenceSimple()
+ */
+cx_attr_nonnull_arg(1, 2, 3, 4)
+CX_EXPORT int cxListDifference(CxList *dst,
+        const CxList *minuend, const CxList *subtrahend,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones elements from a list only if they are also present in another list.
+ *
+ * This function is optimized for the case when both the @p src and the
+ * @p other list are sorted.
+ *
+ * If the destination list already contains elements, the intersection is appended
+ * to that list.
+ *
+ * @param dst the destination list
+ * @param src the list to clone the elements from
+ * @param other the list to check the elements for existence
+ * @param clone_func the clone function for the elements
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListIntersectionSimple()
+ */
+cx_attr_nonnull_arg(1, 2, 3, 4)
+CX_EXPORT int cxListIntersection(CxList *dst, const CxList *src, const CxList *other,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Performs a deep clone of one list into another, skipping duplicates.
+ *
+ * This function is optimized for the case when both the @p src and the
+ * @p other list are sorted.
+ * In that case, the union will also be sorted.
+ *
+ * If the destination list already contains elements, the union is appended
+ * to that list. In that case the destination is not necessarily sorted.
+ *
+ * @param dst the destination list
+ * @param src the primary source list
+ * @param other the other list, where elements are only cloned from
+ * when they are not in @p src
+ * @param clone_func the clone function for the elements
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListUnionSimple()
+ */
+cx_attr_nonnull_arg(1, 2, 3, 4)
+CX_EXPORT int cxListUnion(CxList *dst, const CxList *src, const CxList *other,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Performs a shallow clone of one list into another.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * If the destination list already contains elements, the cloned elements
+ * are appended to that list.
+ *
+ * @attention If the cloned elements need to be destroyed by a destructor
+ * function, you must make sure that the destination list also uses this
+ * destructor function.
+ *
+ * @param dst the destination list
+ * @param src the source list
+ * @retval zero when all elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListClone()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxListCloneSimple(CxList *dst, const CxList *src);
+
+/**
+ * Clones elements from a list only if they are not present in another list.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * If the @p minuend does not contain duplicates, this is equivalent to adding
+ * the set difference to the destination list.
+ *
+ * This function is optimized for the case when both the @p minuend and the
+ * @p subtrahend are sorted.
+ *
+ * @param dst the destination list
+ * @param minuend the list to subtract elements from
+ * @param subtrahend the elements that shall be subtracted
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListDifference()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxListDifferenceSimple(CxList *dst,
+        const CxList *minuend, const CxList *subtrahend);
+
+/**
+ * Clones elements from a list only if they are also present in another list.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * This function is optimized for the case when both the @p src and the
+ * @p other list are sorted.
+ *
+ * If the destination list already contains elements, the intersection is appended
+ * to that list.
+ *
+ * @param dst the destination list
+ * @param src the list to clone the elements from
+ * @param other the list to check the elements for existence
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListIntersection()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other);
+
+/**
+ * Performs a deep clone of one list into another, skipping duplicates.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * This function is optimized for the case when both the @p src and the
+ * @p other list are sorted.
+ * In that case, the union will also be sorted.
+ *
+ * If the destination list already contains elements, the union is appended
+ * to that list. In that case the destination is not necessarily sorted.
+ *
+ * @param dst the destination list
+ * @param src the primary source list
+ * @param other the other list, where elements are only cloned from
+ * when they are not in @p src
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListUnion()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other);
+
+/**
+ * Asks the list to reserve enough memory for a given total number of elements.
+ *
+ * List implementations are free to choose if reserving memory upfront makes
+ * sense.
+ * For example, array-based implementations usually do support reserving memory
+ * for additional elements while linked lists usually don't.
+ *
+ * @note When the requested capacity is smaller than the current size,
+ * this function returns zero without performing any action.
+ *
+ * @param list the list
+ * @param capacity the expected total number of elements
+ * @retval zero on success or when overallocation is not supported
+ * @retval non-zero when an allocation error occurred
+ * @see cxListShrink()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxListReserve(CxList *list, size_t capacity);
+
+/**
+ * Advises the list to free any overallocated memory.
+ *
+ * Lists that do not support overallocation simply return zero.
+ *
+ * This function usually returns zero, except for very special and custom
+ * list and/or allocator implementations where freeing memory can fail.
+ *
+ * @param list the list
+ * @return usually zero
+ */
+cx_attr_nonnull
+CX_EXPORT int cxListShrink(CxList *list);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 0b3060d1ca0bc3326670545e6aaafb2d0ba2c0cc..871bc41716b51e4557958ea92546df3fc97c2548 100644 (file)
 #include "string.h"
 #include "hash_key.h"
 
+#ifndef UCX_LIST_H
+// forward-declare CxList
+typedef struct cx_list_s CxList;
+#endif
+
 #ifdef    __cplusplus
 extern "C" {
 #endif
@@ -404,11 +409,22 @@ CX_EXPORT void *cx_map_get(const CxMap *map, CxHashKey key);
  *
  * @param map (@c CxMap*) the map
  * @param key (any supported key type) the key
- * @return (@c void*) the value
+ * @return (@c void*) the value or @c NULL when no value with that @p key exists
  * @see CX_HASH_KEY()
  */
 #define cxMapGet(map, key) cx_map_get(map, CX_HASH_KEY(key))
 
+/**
+ * Checks if a map contains a specific key.
+ *
+ * @param map (@c CxMap*) the map
+ * @param key (any supported key type) the key
+ * @retval true if the key exists in the map
+ * @retval false if the key does not exist in the map
+ * @see CX_HASH_KEY()
+ */
+#define cxMapContains(map, key) (cxMapGet(map, key) != NULL)
+
 /**
  * Removes a key/value-pair from the map by using the key.
  *
@@ -464,6 +480,246 @@ CX_EXPORT int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
  */
 #define cxMapRemoveAndGet(map, key, targetbuf) cx_map_remove(map, CX_HASH_KEY(key), targetbuf)
 
+
+/**
+ * Performs a deep clone of one map into another.
+ *
+ * If the destination map already contains entries, the cloned entries
+ * are added to that map, possibly overwriting existing elements when
+ * the keys already exist.
+ *
+ * When elements in the destination map need to be replaced, any destructor
+ * function is called on the replaced elements before replacing them.
+ *
+ * @attention If the cloned elements need to be destroyed by a destructor
+ * function, you must make sure that the destination map also uses this
+ * destructor function.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when all elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull_arg(1, 2, 3)
+CX_EXPORT int cxMapClone(CxMap *dst, const CxMap *src,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+
+/**
+ * Clones entries of a map if their key is not present in another map.
+ *
+ * @param dst the destination map
+ * @param minuend the map to subtract the entries from
+ * @param subtrahend the map containing the elements to be subtracted
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull_arg(1, 2, 3, 4)
+CX_EXPORT int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones entries of a map if their key is not present in a list.
+ *
+ * Note that the list must contain keys of type @c CxKey
+ * (or pointers to such keys) and must use @c cx_hash_key_cmp
+ * as the compare function.
+ * Generic key types cannot be processed in this case.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param keys the list of @c CxKey items
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull_arg(1, 2, 3, 4)
+CX_EXPORT int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+
+/**
+ * Clones entries of a map only if their key is present in another map.
+ *
+ * @param dst the destination map
+ * @param src the map to clone the entries from
+ * @param other the map to check for existence of the keys
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull_arg(1, 2, 3, 4)
+CX_EXPORT int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones entries of a map only if their key is present in a list.
+ *
+ * Note that the list must contain keys of type @c CxKey
+ * (or pointers to such keys) and must use @c cx_hash_key_cmp
+ * as the compare function.
+ * Generic key types cannot be processed in this case.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param keys the list of @c CxKey items
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull_arg(1, 2, 3, 4)
+CX_EXPORT int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones entries into a map if their key does not exist yet.
+ *
+ * If you want to calculate the union of two maps into a fresh new map,
+ * you can proceed as follows:
+ * 1. Clone the first map into a fresh, empty map.
+ * 2. Use this function to clone the second map into the result from step 1.
+ *
+ * @param dst the destination map
+ * @param src the map to clone the entries from
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull_arg(1, 2, 3)
+CX_EXPORT int cxMapUnion(CxMap *dst, const CxMap *src,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Performs a shallow clone of one map into another.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * If the destination map already contains entries, the cloned entries
+ * are added to that map, possibly overwriting existing elements when
+ * the keys already exist.
+ *
+ * When elements in the destination map need to be replaced, any destructor
+ * function is called on the replaced elements before replacing them.
+ *
+ * @attention If the cloned elements need to be destroyed by a destructor
+ * function, you must make sure that the destination map also uses this
+ * destructor function.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @retval zero when all elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxMapClone()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxMapCloneSimple(CxMap *dst, const CxMap *src);
+
+/**
+ * Clones entries of a map if their key is not present in another map.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * @param dst the destination map
+ * @param minuend the map to subtract the entries from
+ * @param subtrahend the map containing the elements to be subtracted
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull
+CX_EXPORT int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend);
+
+/**
+ * Clones entries of a map if their key is not present in a list.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * Note that the list must contain keys of type @c CxKey
+ * (or pointers to such keys) and must use @c cx_hash_key_cmp
+ * as the compare function.
+ * Generic key types cannot be processed in this case.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param keys the list of @c CxKey items
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxMapListDifference()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys);
+
+
+/**
+ * Clones entries of a map only if their key is present in another map.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * @param dst the destination map
+ * @param src the map to clone the entries from
+ * @param other the map to check for existence of the keys
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull
+CX_EXPORT int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other);
+
+/**
+ * Clones entries of a map only if their key is present in a list.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * Note that the list must contain keys of type @c CxKey
+ * (or pointers to such keys) and must use @c cx_hash_key_cmp
+ * as the compare function.
+ * Generic key types cannot be processed in this case.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param keys the list of @c CxKey items
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull
+CX_EXPORT int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys);
+
+/**
+ * Clones entries into a map if their key does not exist yet.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * If you want to calculate the union of two maps into a fresh new map,
+ * you can proceed as follows:
+ * 1. Clone the first map into a fresh, empty map.
+ * 2. Use this function to clone the second map into the result from step 1.
+ *
+ * @param dst the destination map
+ * @param src the map to clone the entries from
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+cx_attr_nonnull
+CX_EXPORT int cxMapUnionSimple(CxMap *dst, const CxMap *src);
+
 #ifdef    __cplusplus
 } // extern "C"
 #endif
index 5177ceb7f0b040c44295346d6219bc878b5017a3..4dd8f1765e95942b3adaa2d1c712f7c6483781cb 100644 (file)
@@ -256,7 +256,7 @@ CX_CPPDECL cxstring cx_strcast(const char *str) {
 }
 cx_attr_nodiscard
 CX_CPPDECL cxstring cx_strcast(const unsigned char *str) {
-    return cx_str(static_cast<const char*>(str));
+    return cx_str(reinterpret_cast<const char*>(str));
 }
 extern "C" {
 #else
index 25f8c4b767f546ee3d132169daedbc74e681a327..3eb72995416fb763d8c99ae7f6d53f89934c7691 100644 (file)
@@ -159,7 +159,9 @@ CxHashKey cx_hash_key_u64(uint64_t x) {
     return key;
 }
 
-int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right) {
+int cx_hash_key_cmp(const void *l, const void *r) {
+    const CxHashKey *left = l;
+    const CxHashKey *right = r;
     int d;
     d = cx_vcmp_uint64(left->hash, right->hash);
     if (d != 0) return d;
index 82ae21e62bef69f53c726ba651c1086efc606c91..be02f22daf3af3f236caab8e9470bb2a586196ab 100644 (file)
@@ -86,7 +86,7 @@ static void *cx_hash_map_put(
     struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
     const CxAllocator *allocator = map->collection.allocator;
 
-    unsigned hash = key.hash;
+    uint64_t hash = key.hash;
     if (hash == 0) {
         cx_hash_murmur(&key);
         hash = key.hash;
@@ -203,7 +203,7 @@ static int cx_hash_map_get_remove(
 ) {
     struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
 
-    unsigned hash = key.hash;
+    uint64_t hash = key.hash;
     if (hash == 0) {
         cx_hash_murmur(&key);
         hash = key.hash;
index d4e19f28c0433cede67a3f9b52f1680252794d8a..5343be15c5bb3fa0ccc6e308a73ad563b8908a8b 100644 (file)
@@ -509,6 +509,15 @@ CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type ty
     return iter;
 }
 
+static int cx_kvl_change_capacity(struct cx_list_s *list,
+        cx_attr_unused size_t cap) {
+    // since our backing list is a linked list, we don't need to do much here,
+    // but rehashing the map is quite useful
+    cx_kv_list *kv_list = (cx_kv_list*)list;
+    cxMapRehash(&kv_list->map->map_base.base);
+    return 0;
+}
+
 static cx_list_class cx_kv_list_class = {
     cx_kvl_deallocate,
     cx_kvl_insert_element,
@@ -524,6 +533,7 @@ static cx_list_class cx_kv_list_class = {
     cx_kvl_sort,
     NULL,
     cx_kvl_reverse,
+    cx_kvl_change_capacity,
     cx_kvl_iterator,
 };
 
index 370b349815a05bc9910cdaafbacab85d873eddce..cac413cae1b2f67784282fdb8080777d067bd47d 100644 (file)
@@ -1252,6 +1252,7 @@ static cx_list_class cx_linked_list_class = {
         cx_ll_sort,
         cx_ll_compare,
         cx_ll_reverse,
+        NULL, // no overallocation supported
         cx_ll_iterator,
 };
 
index d4a8b2267585a6dead7349f93cfce8843448f48e..a3a32fbf17031def72ceba68c71a6e5a99131817 100644 (file)
@@ -185,6 +185,14 @@ static void *cx_pl_iter_current(const void *it) {
     return ptr == NULL ? NULL : *ptr;
 }
 
+static int cx_pl_change_capacity(struct cx_list_s *list, size_t cap) {
+    if (list->climpl->change_capacity == NULL) {
+        return 0;
+    } else {
+        return list->climpl->change_capacity(list, cap);
+    }
+}
+
 static struct cx_iterator_s cx_pl_iterator(
         const struct cx_list_s *list,
         size_t index,
@@ -211,6 +219,7 @@ static cx_list_class cx_pointer_list_class = {
         cx_pl_sort,
         cx_pl_compare,
         cx_pl_reverse,
+        cx_pl_change_capacity,
         cx_pl_iterator,
 };
 // </editor-fold>
@@ -267,6 +276,7 @@ static cx_list_class cx_empty_list_class = {
         cx_emptyl_noop,
         NULL,
         cx_emptyl_noop,
+        NULL,
         cx_emptyl_iterator,
 };
 
@@ -804,3 +814,318 @@ void cxListFree(CxList *list) {
     if (list == NULL) return;
     list->cl->deallocate(list);
 }
+
+static void cx_list_pop_uninitialized_elements(CxList *list, size_t n) {
+    cx_destructor_func destr_bak = list->collection.simple_destructor;
+    cx_destructor_func2 destr2_bak = list->collection.advanced_destructor;
+    list->collection.simple_destructor = NULL;
+    list->collection.advanced_destructor = NULL;
+    if (n == 1) {
+        cxListRemove(list, list->collection.size - 1);
+    } else {
+        cxListRemoveArray(list,list->collection.size - n, n);
+    }
+    list->collection.simple_destructor = destr_bak;
+    list->collection.advanced_destructor = destr2_bak;
+}
+
+static void* cx_list_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {
+    size_t elem_size = *(size_t*)data;
+    if (dst == NULL) dst = cxMalloc(al, elem_size);
+    if (dst != NULL) memcpy(dst, src, elem_size);
+    return dst;
+}
+
+#define use_simple_clone_func(list) cx_list_simple_clone_func, NULL, (void*)&((list)->collection.elem_size)
+
+int cxListClone(CxList *dst, const CxList *src, cx_clone_func clone_func,
+        const CxAllocator *clone_allocator, void *data) {
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
+
+    // remember the original size
+    size_t orig_size = dst->collection.size;
+
+    // first, try to allocate the memory in the new list
+    CxIterator empl_iter = cxListEmplaceArray(dst, src->collection.size);
+
+    // get an iterator over the source elements
+    CxIterator src_iter = cxListIterator(src);
+
+    // now clone the elements
+    size_t cloned = empl_iter.elem_count;
+    for (size_t i = 0 ; i < empl_iter.elem_count; i++) {
+        void *src_elem = cxIteratorCurrent(src_iter);
+        void **dest_memory = cxIteratorCurrent(empl_iter);
+        void *target = cxCollectionStoresPointers(dst) ? NULL : dest_memory;
+        void *dest_ptr = clone_func(target, src_elem, clone_allocator, data);
+        if (dest_ptr == NULL) {
+            cloned = i;
+            break;
+        }
+        if (cxCollectionStoresPointers(dst)) {
+            *dest_memory = dest_ptr;
+        }
+        cxIteratorNext(src_iter);
+        cxIteratorNext(empl_iter);
+    }
+
+    // if we could not clone everything, free the allocated memory
+    // (disable the destructors!)
+    if (cloned < src->collection.size) {
+        cx_list_pop_uninitialized_elements(dst,
+            dst->collection.size - cloned - orig_size);
+        return 1;
+    }
+
+    // set the sorted flag when we know it's sorted
+    if (orig_size == 0 && src->collection.sorted) {
+        dst->collection.sorted = true;
+    }
+
+    return 0;
+}
+
+int cxListDifference(CxList *dst,
+        const CxList *minuend, const CxList *subtrahend,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
+
+    // optimize for sorted collections
+    if (cxCollectionSorted(minuend) && cxCollectionSorted(subtrahend)) {
+        bool dst_was_empty = cxCollectionSize(dst) == 0;
+
+        CxIterator min_iter = cxListIterator(minuend);
+        CxIterator sub_iter = cxListIterator(subtrahend);
+        while (cxIteratorValid(min_iter)) {
+            void *min_elem = cxIteratorCurrent(min_iter);
+            void *sub_elem;
+            int d;
+            if (cxIteratorValid(sub_iter)) {
+                sub_elem = cxIteratorCurrent(sub_iter);
+                cx_compare_func cmp = subtrahend->collection.cmpfunc;
+                d = cmp(sub_elem, min_elem);
+            } else {
+                // no more elements in the subtrahend,
+                // i.e., the min_elem is larger than any elem of the subtrahend
+                d = 1;
+            }
+            if (d == 0) {
+                // is contained, so skip it
+                cxIteratorNext(min_iter);
+            } else if (d < 0) {
+                // subtrahend is smaller than minuend,
+                // check the next element
+                cxIteratorNext(sub_iter);
+            } else {
+                // subtrahend is larger than the dst element,
+                // clone the minuend and advance
+                void **dst_mem = cxListEmplace(dst);
+                void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+                void* dst_ptr = clone_func(target, min_elem, clone_allocator, data);
+                if (dst_ptr == NULL) {
+                    cx_list_pop_uninitialized_elements(dst, 1);
+                    return 1;
+                }
+                if (cxCollectionStoresPointers(dst)) {
+                    *dst_mem = dst_ptr;
+                }
+                cxIteratorNext(min_iter);
+            }
+        }
+
+        // if dst was empty, it is now guaranteed to be sorted
+        dst->collection.sorted = dst_was_empty;
+    } else {
+        CxIterator min_iter = cxListIterator(minuend);
+        cx_foreach(void *, elem, min_iter) {
+            if (cxListContains(subtrahend, elem)) {
+                continue;
+            }
+            void **dst_mem = cxListEmplace(dst);
+            void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+            void* dst_ptr = clone_func(target, elem, clone_allocator, data);
+            if (dst_ptr == NULL) {
+                cx_list_pop_uninitialized_elements(dst, 1);
+                return 1;
+            }
+            if (cxCollectionStoresPointers(dst)) {
+                *dst_mem = dst_ptr;
+            }
+        }
+    }
+
+    return 0;
+}
+
+int cxListIntersection(CxList *dst,
+        const CxList *src, const CxList *other,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
+
+    // optimize for sorted collections
+    if (cxCollectionSorted(src) && cxCollectionSorted(other)) {
+        bool dst_was_empty = cxCollectionSize(dst) == 0;
+
+        CxIterator src_iter = cxListIterator(src);
+        CxIterator other_iter = cxListIterator(other);
+        while (cxIteratorValid(src_iter) && cxIteratorValid(other_iter)) {
+            void *src_elem = cxIteratorCurrent(src_iter);
+            void *other_elem = cxIteratorCurrent(other_iter);
+            int d = src->collection.cmpfunc(src_elem, other_elem);
+            if (d == 0) {
+                // is contained, clone it
+                void **dst_mem = cxListEmplace(dst);
+                void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+                void* dst_ptr = clone_func(target, src_elem, clone_allocator, data);
+                if (dst_ptr == NULL) {
+                    cx_list_pop_uninitialized_elements(dst, 1);
+                    return 1;
+                }
+                if (cxCollectionStoresPointers(dst)) {
+                    *dst_mem = dst_ptr;
+                }
+                cxIteratorNext(src_iter);
+            } else if (d < 0) {
+                // the other element is larger, skip the source element
+                cxIteratorNext(src_iter);
+            } else {
+                // the source element is larger, try to find it in the other list
+                cxIteratorNext(other_iter);
+            }
+        }
+
+        // if dst was empty, it is now guaranteed to be sorted
+        dst->collection.sorted = dst_was_empty;
+    } else {
+        CxIterator src_iter = cxListIterator(src);
+        cx_foreach(void *, elem, src_iter) {
+            if (!cxListContains(other, elem)) {
+                continue;
+            }
+            void **dst_mem = cxListEmplace(dst);
+            void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+            void* dst_ptr = clone_func(target, elem, clone_allocator, data);
+            if (dst_ptr == NULL) {
+                cx_list_pop_uninitialized_elements(dst, 1);
+                return 1;
+            }
+            if (cxCollectionStoresPointers(dst)) {
+                *dst_mem = dst_ptr;
+            }
+        }
+    }
+
+    return 0;
+}
+
+int cxListUnion(CxList *dst,
+        const CxList *src, const CxList *other,
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
+
+    // optimize for sorted collections
+    if (cxCollectionSorted(src) && cxCollectionSorted(other)) {
+        bool dst_was_empty = cxCollectionSize(dst) == 0;
+
+        CxIterator src_iter = cxListIterator(src);
+        CxIterator other_iter = cxListIterator(other);
+        while (cxIteratorValid(src_iter) || cxIteratorValid(other_iter)) {
+            void *src_elem, *other_elem;
+            int d;
+            if (!cxIteratorValid(src_iter)) {
+                other_elem = cxIteratorCurrent(other_iter);
+                d = 1;
+            } else if (!cxIteratorValid(other_iter)) {
+                src_elem = cxIteratorCurrent(src_iter);
+                d = -1;
+            } else {
+                src_elem = cxIteratorCurrent(src_iter);
+                other_elem = cxIteratorCurrent(other_iter);
+                d = src->collection.cmpfunc(src_elem, other_elem);
+            }
+            void *clone_from;
+            if (d < 0) {
+                // source element is smaller clone it
+                clone_from = src_elem;
+                cxIteratorNext(src_iter);
+            } else if (d == 0) {
+                // both elements are equal, clone from the source, skip other
+                clone_from = src_elem;
+                cxIteratorNext(src_iter);
+                cxIteratorNext(other_iter);
+            } else {
+                // the other element is smaller, clone it
+                clone_from = other_elem;
+                cxIteratorNext(other_iter);
+            }
+            void **dst_mem = cxListEmplace(dst);
+            void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+            void* dst_ptr = clone_func(target, clone_from, clone_allocator, data);
+            if (dst_ptr == NULL) {
+                cx_list_pop_uninitialized_elements(dst, 1);
+                return 1;
+            }
+            if (cxCollectionStoresPointers(dst)) {
+                *dst_mem = dst_ptr;
+            }
+        }
+
+        // if dst was empty, it is now guaranteed to be sorted
+        dst->collection.sorted = dst_was_empty;
+    } else {
+        if (cxListClone(dst, src, clone_func, clone_allocator, data)) {
+            return 1;
+        }
+        CxIterator other_iter = cxListIterator(other);
+        cx_foreach(void *, elem, other_iter) {
+            if (cxListContains(src, elem)) {
+                continue;
+            }
+            void **dst_mem = cxListEmplace(dst);
+            void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+            void* dst_ptr = clone_func(target, elem, clone_allocator, data);
+            if (dst_ptr == NULL) {
+                cx_list_pop_uninitialized_elements(dst, 1);
+                return 1;
+            }
+            if (cxCollectionStoresPointers(dst)) {
+                *dst_mem = dst_ptr;
+            }
+        }
+    }
+
+    return 0;
+}
+
+int cxListCloneSimple(CxList *dst, const CxList *src) {
+    return cxListClone(dst, src, use_simple_clone_func(src));
+}
+
+int cxListDifferenceSimple(CxList *dst, const CxList *minuend, const CxList *subtrahend) {
+    return cxListDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend));
+}
+
+int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other) {
+    return cxListIntersection(dst, src, other, use_simple_clone_func(src));
+}
+
+int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other) {
+    return cxListUnion(dst, src, other, use_simple_clone_func(src));
+}
+
+int cxListReserve(CxList *list, size_t capacity) {
+    if (list->cl->change_capacity == NULL) {
+        return 0;
+    }
+    if (capacity <= cxCollectionSize(list)) {
+        return 0;
+    }
+    return list->cl->change_capacity(list, capacity);
+}
+
+int cxListShrink(CxList *list) {
+    if (list->cl->change_capacity == NULL) {
+        return 0;
+    }
+    return list->cl->change_capacity(list, cxCollectionSize(list));
+}
index d0c41f4a8377bc09f22c6ae5ea2bb10e4ce92b4f..a83df9edd2518e3f1c918dce73d114b2dbe671f1 100644 (file)
--- a/ucx/map.c
+++ b/ucx/map.c
@@ -29,6 +29,8 @@
 #include "cx/map.h"\r
 #include <string.h>\r
 \r
+#include "cx/list.h"\r
+\r
 // <editor-fold desc="empty map implementation">\r
 \r
 static void cx_empty_map_noop(cx_attr_unused CxMap *map) {\r
@@ -127,3 +129,200 @@ void cxMapFree(CxMap *map) {
     if (map == NULL) return;\r
     map->cl->deallocate(map);\r
 }\r
+\r
+static void cx_map_remove_uninitialized_entry(CxMap *map, CxHashKey key) {\r
+    cx_destructor_func destr_bak = map->collection.simple_destructor;\r
+    cx_destructor_func2 destr2_bak = map->collection.advanced_destructor;\r
+    map->collection.simple_destructor = NULL;\r
+    map->collection.advanced_destructor = NULL;\r
+    cxMapRemove(map, key);\r
+    map->collection.simple_destructor = destr_bak;\r
+    map->collection.advanced_destructor = destr2_bak;\r
+}\r
+\r
+static void* cx_map_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {\r
+    size_t elem_size = *(size_t*)data;\r
+    if (dst == NULL) dst = cxMalloc(al, elem_size);\r
+    if (dst != NULL) memcpy(dst, src, elem_size);\r
+    return dst;\r
+}\r
+\r
+#define use_simple_clone_func(map) cx_map_simple_clone_func, NULL, (void*)&((map)->collection.elem_size)\r
+\r
+int cxMapClone(CxMap *dst, const CxMap *src, cx_clone_func clone_func,\r
+        const CxAllocator *clone_allocator, void *data) {\r
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+    CxMapIterator src_iter = cxMapIterator(src);\r
+    for (size_t i = 0; i < cxMapSize(src); i++) {\r
+        const CxMapEntry *entry = cxIteratorCurrent(src_iter);\r
+        void **dst_mem = cxMapEmplace(dst, *(entry->key));\r
+        if (dst_mem == NULL) {\r
+            return 1; // LCOV_EXCL_LINE\r
+        }\r
+        void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+        void *dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+        if (dst_ptr == NULL) {\r
+            cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+            return 1;\r
+        }\r
+        if (cxCollectionStoresPointers(dst)) {\r
+            *dst_mem = dst_ptr;\r
+        }\r
+        cxIteratorNext(src_iter);\r
+    }\r
+    return 0;\r
+}\r
+\r
+int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend,\r
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+    CxMapIterator src_iter = cxMapIterator(minuend);\r
+    cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+        if (cxMapContains(subtrahend, *entry->key)) {\r
+            continue;\r
+        }\r
+        void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+        if (dst_mem == NULL) {\r
+            return 1; // LCOV_EXCL_LINE\r
+        }\r
+        void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+        void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+        if (dst_ptr == NULL) {\r
+            cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+            return 1;\r
+        }\r
+        if (cxCollectionStoresPointers(dst)) {\r
+            *dst_mem = dst_ptr;\r
+        }\r
+    }\r
+    return 0;\r
+}\r
+\r
+int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys,\r
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+    CxMapIterator src_iter = cxMapIterator(src);\r
+    cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+        if (cxListContains(keys, entry->key)) {\r
+            continue;\r
+        }\r
+        void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+        if (dst_mem == NULL) {\r
+            return 1; // LCOV_EXCL_LINE\r
+        }\r
+        void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+        void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+        if (dst_ptr == NULL) {\r
+            cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+            return 1;\r
+        }\r
+        if (cxCollectionStoresPointers(dst)) {\r
+            *dst_mem = dst_ptr;\r
+        }\r
+    }\r
+    return 0;\r
+}\r
+\r
+int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other,\r
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+    CxMapIterator src_iter = cxMapIterator(src);\r
+    cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+        if (!cxMapContains(other, *entry->key)) {\r
+            continue;\r
+        }\r
+        void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+        if (dst_mem == NULL) {\r
+            return 1; // LCOV_EXCL_LINE\r
+        }\r
+        void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+        void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+        if (dst_ptr == NULL) {\r
+            cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+            return 1;\r
+        }\r
+        if (cxCollectionStoresPointers(dst)) {\r
+            *dst_mem = dst_ptr;\r
+        }\r
+    }\r
+    return 0;\r
+}\r
+\r
+int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys,\r
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+    CxMapIterator src_iter = cxMapIterator(src);\r
+    cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+        if (!cxListContains(keys, entry->key)) {\r
+            continue;\r
+        }\r
+        void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+        if (dst_mem == NULL) {\r
+            return 1; // LCOV_EXCL_LINE\r
+        }\r
+        void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+        void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+        if (dst_ptr == NULL) {\r
+            cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+            return 1;\r
+        }\r
+        if (cxCollectionStoresPointers(dst)) {\r
+            *dst_mem = dst_ptr;\r
+        }\r
+    }\r
+    return 0;\r
+}\r
+\r
+int cxMapUnion(CxMap *dst, const CxMap *src,\r
+        cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+    if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+    CxMapIterator src_iter = cxMapIterator(src);\r
+    cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+        if (cxMapContains(dst, *entry->key)) {\r
+            continue;\r
+        }\r
+        void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+        if (dst_mem == NULL) {\r
+            return 1; // LCOV_EXCL_LINE\r
+        }\r
+        void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+        void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+        if (dst_ptr == NULL) {\r
+            cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+            return 1;\r
+        }\r
+        if (cxCollectionStoresPointers(dst)) {\r
+            *dst_mem = dst_ptr;\r
+        }\r
+    }\r
+    return 0;\r
+}\r
+\r
+int cxMapCloneSimple(CxMap *dst, const CxMap *src) {\r
+    return cxMapClone(dst, src, use_simple_clone_func(src));\r
+}\r
+\r
+int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) {\r
+    return cxMapDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend));\r
+}\r
+\r
+int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys) {\r
+    return cxMapListDifference(dst, src, keys, use_simple_clone_func(src));\r
+}\r
+\r
+int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other) {\r
+    return cxMapIntersection(dst, src, other, use_simple_clone_func(src));\r
+}\r
+\r
+int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys) {\r
+    return cxMapListIntersection(dst, src, keys, use_simple_clone_func(src));\r
+}\r
+\r
+int cxMapUnionSimple(CxMap *dst, const CxMap *src) {\r
+    return cxMapUnion(dst, src, use_simple_clone_func(src));\r
+}\r
index fab6427edd150a37c232be90acd2a7cb3a526269..363e8beb7a90323c4b3f53d36ace208dac9ad9a1 100644 (file)
@@ -116,6 +116,9 @@ static void cx_mempool_free_simple(
     if (!ptr) return;
     struct cx_mempool_s *pool = p;
 
+    cx_destructor_func destr = pool->destr;
+    cx_destructor_func2 destr2 = pool->destr2;
+
     struct cx_mempool_memory_s *mem =
         (void*) ((char *) ptr - sizeof(struct cx_mempool_memory_s));
 
@@ -124,11 +127,11 @@ static void cx_mempool_free_simple(
             if (mem->destructor) {
                 mem->destructor(mem->c);
             }
-            if (pool->destr) {
-                pool->destr(mem->c);
+            if (destr != NULL) {
+                destr(mem->c);
             }
-            if (pool->destr2) {
-                pool->destr2(pool->destr2_data, mem->c);
+            if (destr2 != NULL) {
+                destr2(pool->destr2_data, mem->c);
             }
             cxFree(pool->base_allocator, mem);
             size_t last_index = pool->size - 1;
@@ -179,18 +182,18 @@ static void *cx_mempool_realloc_simple(
 }
 
 static void cx_mempool_free_all_simple(const struct cx_mempool_s *pool) {
-    const bool has_destr = pool->destr;
-    const bool has_destr2 = pool->destr2;
+    cx_destructor_func destr = pool->destr;
+    cx_destructor_func2 destr2 = pool->destr2;
     for (size_t i = 0; i < pool->size; i++) {
         struct cx_mempool_memory_s *mem = pool->data[i];
         if (mem->destructor) {
             mem->destructor(mem->c);
         }
-        if (has_destr) {
-            pool->destr(mem->c);
+        if (destr != NULL) {
+            destr(mem->c);
         }
-        if (has_destr2) {
-            pool->destr2(pool->destr2_data, mem->c);
+        if (destr2 != NULL) {
+            destr2(pool->destr2_data, mem->c);
         }
         cxFree(pool->base_allocator, mem);
     }
@@ -247,6 +250,9 @@ static void cx_mempool_free_advanced(
     if (!ptr) return;
     struct cx_mempool_s *pool = p;
 
+    cx_destructor_func destr = pool->destr;
+    cx_destructor_func2 destr2 = pool->destr2;
+
     struct cx_mempool_memory2_s *mem =
         (void*) ((char *) ptr - sizeof(struct cx_mempool_memory2_s));
 
@@ -255,11 +261,11 @@ static void cx_mempool_free_advanced(
             if (mem->destructor) {
                 mem->destructor(mem->data, mem->c);
             }
-            if (pool->destr) {
-                pool->destr(mem->c);
+            if (destr != NULL) {
+                destr(mem->c);
             }
-            if (pool->destr2) {
-                pool->destr2(pool->destr2_data, mem->c);
+            if (destr2 != NULL) {
+                destr2(pool->destr2_data, mem->c);
             }
             cxFree(pool->base_allocator, mem);
             size_t last_index = pool->size - 1;
@@ -310,18 +316,18 @@ static void *cx_mempool_realloc_advanced(
 }
 
 static void cx_mempool_free_all_advanced(const struct cx_mempool_s *pool) {
-    const bool has_destr = pool->destr;
-    const bool has_destr2 = pool->destr2;
+    cx_destructor_func destr = pool->destr;
+    cx_destructor_func2 destr2 = pool->destr2;
     for (size_t i = 0; i < pool->size; i++) {
         struct cx_mempool_memory2_s *mem = pool->data[i];
         if (mem->destructor) {
             mem->destructor(mem->data, mem->c);
         }
-        if (has_destr) {
-            pool->destr(mem->c);
+        if (destr != NULL) {
+            destr(mem->c);
         }
-        if (has_destr2) {
-            pool->destr2(pool->destr2_data, mem->c);
+        if (destr2 != NULL) {
+            destr2(pool->destr2_data, mem->c);
         }
         cxFree(pool->base_allocator, mem);
     }
@@ -376,13 +382,16 @@ static void cx_mempool_free_pure(
     if (!ptr) return;
     struct cx_mempool_s *pool = p;
 
+    cx_destructor_func destr = pool->destr;
+    cx_destructor_func2 destr2 = pool->destr2;
+
     for (size_t i = 0; i < pool->size; i++) {
         if (ptr == pool->data[i]) {
-            if (pool->destr) {
-                pool->destr(ptr);
+            if (destr != NULL) {
+                destr(ptr);
             }
-            if (pool->destr2) {
-                pool->destr2(pool->destr2_data, ptr);
+            if (destr2 != NULL) {
+                destr2(pool->destr2_data, ptr);
             }
             cxFree(pool->base_allocator, ptr);
             size_t last_index = pool->size - 1;
@@ -427,15 +436,15 @@ static void *cx_mempool_realloc_pure(
 }
 
 static void cx_mempool_free_all_pure(const struct cx_mempool_s *pool) {
-    const bool has_destr = pool->destr;
-    const bool has_destr2 = pool->destr2;
+    cx_destructor_func destr = pool->destr;
+    cx_destructor_func2 destr2 = pool->destr2;
     for (size_t i = 0; i < pool->size; i++) {
         void *mem = pool->data[i];
-        if (has_destr) {
-            pool->destr(mem);
+        if (destr != NULL) {
+            destr(mem);
         }
-        if (has_destr2) {
-            pool->destr2(pool->destr2_data, mem);
+        if (destr2 != NULL) {
+            destr2(pool->destr2_data, mem);
         }
         cxFree(pool->base_allocator, mem);
     }
index 73911a5666f0ca2ce81b4028bbaec4db663bea9f..0079ff0019e00b2c4f098bd20c031a480faf1ec9 100644 (file)
@@ -114,7 +114,7 @@ void uic_context_attach_document(UiContext *ctx, void *document) {
             UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key);
             if(docvar) {
                 // bind var to document var
-                uic_copy_binding(var, docvar, TRUE);
+                uic_copy_var_binding(var, docvar, TRUE);
                 cxIteratorFlagRemoval(i);
             }
         }
@@ -124,16 +124,19 @@ void uic_context_attach_document(UiContext *ctx, void *document) {
 }
 
 static void uic_context_unbind_vars(UiContext *ctx) {
+    ui_onchange_events_enable(FALSE);
     CxMapIterator mi = cxMapIterator(ctx->vars);
     cx_foreach(CxMapEntry*, entry, mi) {
+        printf("detach %.*s\n", (int)entry->key->len, (char*)entry->key->data);
         UiVar *var = entry->value;
         // var->from && var->from_ctx && var->from_ctx != ctx
         uic_save_var(var);
         if(var->from) {
-            uic_copy_binding(var, var->from, FALSE);
+            uic_copy_var_binding(var, var->from, FALSE);
             cxMapPut(var->from->from_ctx->vars, *entry->key, var->from);
             var->from = NULL;
         }
+        uic_unbind_var(var);
     }
     
     if(ctx->documents) {
@@ -143,6 +146,8 @@ static void uic_context_unbind_vars(UiContext *ctx) {
             uic_context_unbind_vars(subctx);
         }
     }
+    
+    ui_onchange_events_enable(TRUE);
 }
 
 void uic_context_detach_document(UiContext *ctx, void *document) {
@@ -213,8 +218,9 @@ UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) {
     var->original_value = NULL;
     var->from = NULL;
     var->from_ctx = ctx;
-
-    cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var);
+    var->bound = FALSE;
+    
+    cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var); // TODO: use another destructor that cleans the value (UiString free, UiText destroy, ...)
 
     cxMapPut(ctx->vars, name, var);
     
@@ -266,6 +272,40 @@ void* uic_create_value(UiContext *ctx, UiVarType type) {
     return val;
 }
 
+// destroys a value, that was created by uic_create_value
+void uic_destroy_value(UiContext *ctx, UiVarType type, void *value) {
+    switch(type) {
+        default: {
+            ui_free(ctx, value);
+            break;
+        }
+        case UI_VAR_SPECIAL: break;
+        case UI_VAR_STRING: {
+            UiString *s = value;
+            if(s->value.free) {
+                s->value.free(s->value.ptr);
+            }
+            ui_free(ctx, value);
+        }
+        case UI_VAR_TEXT: {
+            UiText *t = value;
+            if(t->value.free) {
+                t->value.free(t->value.ptr);
+                t->value.free = NULL;
+                t->value.ptr = NULL;
+            }
+            if(t->destroy) {
+                t->destroy(t);
+            }
+            ui_free(ctx, value);
+        }
+        case UI_VAR_LIST: {
+            ui_list_free(ctx, value);
+            break;
+        }
+    }
+}
+
 
 UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type) {
     if (value) {
@@ -278,7 +318,7 @@ UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, cons
 }
 
 
-void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
+void uic_copy_var_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
     // check type
     if(from->type != to->type) {
         fprintf(stderr, "UI Error: var has incompatible type.\n");
@@ -301,32 +341,36 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
         }
     }
     
-    ui_setop_enable(TRUE);
+    uic_copy_value_binding(from->type, fromvalue, tovalue);
+}
+
+void uic_copy_value_binding(UiVarType type, void *from, void *to) {
+     ui_setop_enable(TRUE);
     
     // copy binding
-    // we don't copy the observer, because the from var has never one
-    switch(from->type) {
+    // we don't copy the observer, because the from value never has oberservers
+    switch(type) {
         default: fprintf(stderr, "uic_copy_binding: wtf!\n"); break;
         case UI_VAR_SPECIAL: break;
         case UI_VAR_INTEGER: {
-            UiInteger *f = fromvalue;
-            UiInteger *t = tovalue;
+            UiInteger *f = from;
+            UiInteger *t = to;
             if(!f->obj) break;
             uic_int_copy(f, t);
             t->set(t, t->value);
             break;
         }
         case UI_VAR_DOUBLE: {
-            UiDouble *f = fromvalue;
-            UiDouble *t = tovalue;
+            UiDouble *f = from;
+            UiDouble *t = to;
             if(!f->obj) break;
             uic_double_copy(f, t);
             t->set(t, t->value);
             break;
         }
         case UI_VAR_STRING: {
-            UiString *f = fromvalue;
-            UiString *t = tovalue;
+            UiString *f = from;
+            UiString *t = to;
             if(!f->obj) break;
             uic_string_copy(f, t);
             char *tvalue = t->value.ptr ? t->value.ptr : "";
@@ -335,23 +379,23 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
             break;
         }
         case UI_VAR_TEXT: {
-            UiText *f = fromvalue;
-            UiText *t = tovalue;
+            UiText *f = from;
+            UiText *t = to;
             if(!f->obj) break;
             uic_text_copy(f, t);
             t->restore(t);
             break;
         }
         case UI_VAR_LIST: {         
-            UiList *f = fromvalue;
-            UiList *t = tovalue;
+            UiList *f = from;
+            UiList *t = to;
             uic_list_copy(f, t);
             ui_list_update(t);
             break;
         }
         case UI_VAR_RANGE: {
-            UiRange *f = fromvalue;
-            UiRange *t = tovalue;
+            UiRange *f = from;
+            UiRange *t = to;
             if(!f->obj) break;
             uic_range_copy(f, t);
             t->setextent(t, t->extent);
@@ -360,8 +404,8 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) {
             break;
         }
         case UI_VAR_GENERIC: {
-            UiGeneric *f = fromvalue;
-            UiGeneric *t = tovalue;
+            UiGeneric *f = from;
+            UiGeneric *t = to;
             if(!f->obj) break;
             uic_generic_copy(f, t);
             t->set(t, t->value, t->type);
@@ -398,58 +442,47 @@ void uic_unbind_var(UiVar *var) {
     }
 }
 
+const char *uic_type2str(UiVarType type) {
+    switch(type) {
+        default: return "";
+        case UI_VAR_INTEGER: return "int";
+        case UI_VAR_DOUBLE: return "double";
+        case UI_VAR_STRING: return "string";
+        case UI_VAR_TEXT: return "text";
+        case UI_VAR_LIST: return "list";
+        case UI_VAR_RANGE: return "range";
+        case UI_VAR_GENERIC: return "generic";
+    }
+}
+
 void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value) {
-    // TODO: do we need/want this? Why adding vars to a context after
-    // widgets reference these? Workarounds:
-    // 1. add vars to ctx before creating ui
-    // 2. create ui, create new document with vars, attach doc
-    // also it would be possible to create a function, that scans unbound vars
-    // and connects them to available vars
-    /*
-    UiContext *rootctx = uic_root_context(ctx); 
-    UiVar *b = NULL;
-    if(rootctx->bound) {
-        // some widgets are already bound to some vars
-        b = ucx_map_cstr_get(rootctx->bound, name);
-        if(b) {
-            // a widget is bound to a var with this name
-            // if ctx is the root context we can remove the var from bound
-            // because set_doc or detach can't fuck things up
-            if(ctx == rootctx) {
-                ucx_map_cstr_remove(ctx->bound, name);
-                // TODO: free stuff
-            }
+    UiVar *var = cxMapGet(ctx->vars, name);
+    if(!var) {
+        // create new var and add it to the context var map
+        var = ui_malloc(ctx, sizeof(UiVar));
+        cxMapPut(ctx->vars, name, var);
+    } else {
+        // override var with new value
+        if(var->type != type) {
+            fprintf(stderr, "Error: var %s type mismatch: %s - %s\n", name, uic_type2str(var->type), type);
+            return;
+        }
+        if(var->bound) {
+            fprintf(stderr, "Error: var %s already bound\n", name);
+            return;
         }
+        UiInteger *prev_value = var->value;
+        uic_copy_value_binding(type, prev_value, value);
+        uic_destroy_value(var->from_ctx, var->type, var->value);
     }
-    */
     
-    // create new var and add it to doc's vars
-    UiVar *var = ui_malloc(ctx, sizeof(UiVar));
     var->type = type;
     var->value = value;
     var->from = NULL;
     var->from_ctx = ctx;
-    size_t oldcount = cxMapSize(ctx->vars);
-    cxMapPut(ctx->vars, name, var);
-    if(cxMapSize(ctx->vars) != oldcount + 1) {
-        fprintf(stderr, "UiError: var '%s' already exists\n", name);
-    }
-    
-    // TODO: remove?
-    // a widget is already bound to a var with this name
-    // copy the binding (like uic_context_set_document)
-    /*
-    if(b) {
-        uic_copy_binding(b, var, TRUE);
-    }
-    */
-}
-
-void uic_remove_bound_var(UiContext *ctx, UiVar *var) {
-    // TODO
+    var->bound = TRUE;
 }
 
-
 // public API
 
 void ui_attach_document(UiContext *ctx, void *document) {
index f5170902d2a5a2c657936a9af0cc047fb929f76b..15e7700386ceb573cf2935a65f7f4b33374a9f3d 100644 (file)
@@ -97,6 +97,7 @@ struct UiVar {
     UiVarType type;
     UiVar     *from;
     UiContext *from_ctx;
+    UiBool    bound;
 };
 
 struct UiGroupWidget {
@@ -130,17 +131,18 @@ UiVar* uic_get_var(UiContext *ctx, const char *name);
 UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type);
 UiVar* uic_create_value_var(UiContext *ctx, void *value);
 void* uic_create_value(UiContext *ctx, UiVarType type);
+void uic_destroy_value(UiContext *ctx, UiVarType type, void *value);
 
 UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type);
 
-void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc);
+void uic_copy_var_binding(UiVar *from, UiVar *to, UiBool copytodoc);
+void uic_copy_value_binding(UiVarType type, void *from, void *to);
 void uic_save_var(UiVar *var);
 void uic_unbind_var(UiVar *var);
+const char *uic_type2str(UiVarType type);
 
 void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value);
 
-void uic_remove_bound_var(UiContext *ctx, UiVar *var);
-
 size_t uic_group_array_size(const int *groups);
 void uic_check_group_widgets(UiContext *ctx);
 void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups);
index cdf3a8ca421e821fac558023eef84f43f12d457e..b0cfc46bbe59052c37abde53c0616815aea4a55d 100644 (file)
@@ -41,6 +41,22 @@ static UiMenuBuilder global_builder;
 
 static int menu_item_counter = 0;
 
+static void *tmp_eventdata;
+static int tmp_eventdata_type;
+
+void uic_set_tmp_eventdata(void *eventdata, int type) {
+    tmp_eventdata = eventdata;
+    tmp_eventdata_type = type;
+}
+
+void* uic_get_tmp_eventdata(void) {
+    return tmp_eventdata;
+}
+
+int uic_get_tmp_eventdata_type(void) {
+    return tmp_eventdata_type;
+}
+
 void uic_menu_init(void) {
     global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
     current_builder = &global_builder;
index e9dce38f518b0d0aa5ce9c705adece897da085b0..f44f6b4f9504333acdae7bc1fa6debe34e88cca6 100644 (file)
@@ -131,6 +131,10 @@ void uic_add_menu_to_stack(UiMenu* menu);
 
 int* uic_copy_groups(const int* groups, size_t *ngroups);
 
+void uic_set_tmp_eventdata(void *eventdata, int type);
+void* uic_get_tmp_eventdata(void);
+int uic_get_tmp_eventdata_type(void);
+
 #ifdef __cplusplus
 }
 #endif
index 57ad68ee7ca9321d8530d769045eb4bf785095d4..53fbbec1f4ce41b28a41d7a8d2b00ecef31b3bac 100644 (file)
@@ -40,6 +40,8 @@
 
 static ui_list_init_func default_list_init;
 static void *default_list_init_userdata;
+static ui_list_destroy_func default_list_destroy;
+static void *default_list_destroy_userdata;
 
 UiObserver* ui_observer_new(ui_callback f, void *data) {
     UiObserver *observer = malloc(sizeof(UiObserver));
@@ -105,6 +107,11 @@ void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused) {
     list->count = ui_list_count;
 }
 
+void uic_ucx_list_destroy(UiContext *ctx, UiList *list, void *unused) {
+    cxListFree(list->data);
+    ui_free(ctx, list);
+}
+
 UiList* ui_list_new(UiContext *ctx, const char *name) {
     return ui_list_new2(ctx, name, default_list_init ? default_list_init : uic_ucx_list_init, default_list_init_userdata);
 }
@@ -121,9 +128,13 @@ UiList* ui_list_new2(UiContext *ctx, const char *name, ui_list_init_func listini
     return list;
 }
 
-void ui_list_free(UiList *list) {
-    cxListFree(list->data);
-    free(list);
+void ui_list_free(UiContext *ctx, UiList *list) {
+    if(!default_list_destroy) {
+        uic_ucx_list_destroy(ctx, list, NULL);
+    } else {
+        default_list_destroy(ctx, list, default_list_destroy_userdata);
+    }
+    
 }
 
 void* ui_list_first(UiList *list) {
@@ -787,6 +798,7 @@ void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObser
 }
 
 static int ui_set_op = 0;
+static int ui_onchange_events_enabled = TRUE;
 
 void ui_setop_enable(int set) {
     ui_set_op = set;
@@ -796,6 +808,14 @@ int ui_get_setop(void) {
     return ui_set_op;
 }
 
+void ui_onchange_events_enable(UiBool enable) {
+    ui_onchange_events_enabled = enable;
+}
+
+UiBool ui_onchange_events_is_enabled(void) {
+    return ui_onchange_events_enabled;
+}
+
 /* ---------------- List initializers and wrapper functions ---------------- */
 
 void ui_global_list_initializer(ui_list_init_func func, void *userdata) {
@@ -803,6 +823,11 @@ void ui_global_list_initializer(ui_list_init_func func, void *userdata) {
     default_list_init_userdata = userdata;
 }
 
+void ui_global_list_destructor(ui_list_destroy_func func, void *userdata) {
+    default_list_destroy = func;
+    default_list_destroy_userdata = userdata;
+}
+
 void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list)) {
     list->first = first;
 }
index 46d6c2e2f11a35f7890f9ffb13bf8159924758c6..2f4a5249ea8bd3e53de2e1f5608c2ba2e4054e35 100644 (file)
@@ -36,6 +36,7 @@ extern "C" {
 #endif
     
 void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused);
+void uic_ucx_list_destroy(UiContext *ctx, UiList *list, void *unused);
     
 void uic_int_copy(UiInteger *from, UiInteger *to);
 void uic_double_copy(UiDouble *from, UiDouble *to);
index 50ecf407f9e15928741a8e9d857de6cd26f768f8..575dd2989f65b13ddec22503fd89ad0c612f3947 100644 (file)
@@ -125,6 +125,11 @@ void ui_srclist_remove(UiList *list, int index) {
     cxListRemove(cxlist, index);
 }
 
+void ui_srclist_swap(UiList *list, int i1, int i2) {
+    CxList *cxlist = list->data;
+    cxListSwap(cxlist, i1, i2);
+}
+
 void ui_srclist_clear(UiList *list) {
     CxList *cxlist = list->data;
     cxListClear(cxlist);
index 88fd8c813b538aabd1100b85c8fa0d7103ae748c..6ce7c9e236cb55ba8e1f7a377c83f9fbe9d3a9ee 100644 (file)
@@ -56,6 +56,7 @@ UIEXPORT UiList* ui_srclist_new(UiContext *ctx, const char *name);
 UIEXPORT void ui_srclist_add(UiList *list, UiSubList *item);
 UIEXPORT void ui_srclist_insert(UiList *list, int index, UiSubList *item);
 UIEXPORT void ui_srclist_remove(UiList *list, int index);
+UIEXPORT void ui_srclist_swap(UiList *list, int i1, int i2);
 UIEXPORT void ui_srclist_clear(UiList *list);
 UIEXPORT int ui_srclist_size(UiList *list);
 UIEXPORT void ui_srclist_generate_sublist_num_data(UiList *list);
index e4612bfbfb3ad3ab705657a262ae352295a161e9..f793b2ca8634f7ddb1f030023f008efa937e6d0f 100644 (file)
@@ -2318,19 +2318,19 @@ void ui_listbox_update(UiListBox *listbox, int from, int to) {
 static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) {
     UiListBoxSubList *sublist = data->customdata0;
     
-    UiSubListEventData eventdata;
-    eventdata.list = sublist->var->value;
-    eventdata.sublist_index = sublist->index;
-    eventdata.row_index = data->value0;
-    eventdata.sublist_userdata = sublist->userdata;
-    eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index);
-    eventdata.event_data = data->customdata2;
+    UiSubListEventData *eventdata = &sublist->listbox->current_eventdata;
+    eventdata->list = sublist->var->value;
+    eventdata->sublist_index = sublist->index;
+    eventdata->row_index = data->value0;
+    eventdata->sublist_userdata = sublist->userdata;
+    eventdata->row_data = eventdata->list->get(eventdata->list, eventdata->row_index);
+    eventdata->event_data = data->customdata2;
     
     UiEvent event;
     event.obj = data->obj;
     event.window = event.obj->window;
     event.document = event.obj->ctx->document;
-    event.eventdata = &eventdata;
+    event.eventdata = eventdata;
     event.eventdatatype = UI_EVENT_DATA_SUBLIST;
     event.intval = data->value0;
     event.set = ui_get_setop();
@@ -2340,13 +2340,15 @@ static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) {
     }
     
     if(data->customdata3) {
+        uic_set_tmp_eventdata(eventdata, UI_EVENT_DATA_SUBLIST);
+        
         UIMENU menu = data->customdata3;
         g_object_set_data(G_OBJECT(button), "ui-button-popup", menu);
         gtk_popover_popup(GTK_POPOVER(menu));
     }
 }
 
-#if GTK_CHECK_VERSION(3, 0, 0)
+#if GTK_CHECK_VERSION(4, 0, 0)
 static void button_popover_closed(GtkPopover *popover, GtkWidget *button) {
     g_object_set_data(G_OBJECT(button), "ui-button-popup", NULL);
     if(g_object_get_data(G_OBJECT(button), "ui-button-invisible")) {
@@ -2354,6 +2356,14 @@ static void button_popover_closed(GtkPopover *popover, GtkWidget *button) {
         gtk_widget_set_visible(button, FALSE);
     }
 }
+#else
+static void popup_hide(GtkWidget *self, GtkWidget *button) {
+    g_object_set_data(G_OBJECT(button), "ui-button-popup", NULL);
+    if(g_object_get_data(G_OBJECT(button), "ui-button-invisible")) {
+        g_object_set_data(G_OBJECT(button), "ui-button-invisible", NULL);
+        gtk_widget_set_visible(button, FALSE);
+    }
+}
 #endif
 
 static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
@@ -2433,7 +2443,11 @@ static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubLis
         if(item->button_menu) {
             UIMENU menu = ui_contextmenu_create(item->button_menu, listbox->obj, button);
             event->customdata3 = menu;
-            g_signal_connect(menu, "closed", G_CALLBACK(button_popover_closed), button);
+#if GTK_CHECK_VERSION(4, 0, 0)
+            g_signal_connect(menu, "closed", G_CALLBACK(button_popover_closed), button);        
+#else
+            g_signal_connect(menu, "hide", G_CALLBACK(popup_hide), button);        
+#endif
             ui_menubuilder_unref(item->button_menu);
         }
     }
index 06f7fac6d7db4fe9e11640002afb53c3f4fae5b5..1aaf89bd7adaebf1a4f9f7157248886eaccbfee4 100644 (file)
@@ -127,6 +127,7 @@ struct UiListBox {
     void                     *onbuttonclickdata;
     GtkListBoxRow            *first_row;
     UiBool                   header_is_item;
+    UiSubListEventData       current_eventdata;
 };
 
 
index c399d85726579a886daaf5731abc25264ceed6d1..74597ae6462f5ffdea62f85ce0471a1c9b2e8ee4 100644 (file)
@@ -284,6 +284,7 @@ void ui_update_menuitem_list(UiActiveMenuItemList *list) {
             event->callback = list->callback;
             event->value = i - 1;
             event->customdata = elm;
+            event->customint = UI_EVENT_DATA_LIST_ELM;
 
             g_signal_connect(
                 widget,
@@ -309,9 +310,17 @@ void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event) {
     evt.obj = event->obj;
     evt.window = event->obj->window;
     evt.document = event->obj->ctx->document;
+    if(event->customdata) {
+        evt.eventdata = event->customdata;
+        evt.eventdatatype = event->customint;
+    } else {
+        evt.eventdata = uic_get_tmp_eventdata();
+        evt.eventdatatype = uic_get_tmp_eventdata_type();
+    }
     evt.eventdata = event->customdata;
     evt.intval = event->value;
-    event->callback(&evt, event->userdata);    
+    event->callback(&evt, event->userdata);   
+    uic_set_tmp_eventdata(NULL, 0);
 }
 
 void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event) {
@@ -747,10 +756,16 @@ void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEvent
     evt.obj = event->obj;
     evt.window = event->obj->window;
     evt.document = event->obj->ctx->document;
-    evt.eventdata = event->customdata;
-    evt.eventdatatype = event->customint;
+    if(event->customdata) {
+        evt.eventdata = event->customdata;
+        evt.eventdatatype = event->customint;
+    } else {
+        evt.eventdata = uic_get_tmp_eventdata();
+        evt.eventdatatype = uic_get_tmp_eventdata_type();
+    }
     evt.intval = intval;
-    event->callback(&evt, event->userdata);    
+    event->callback(&evt, event->userdata);
+    uic_set_tmp_eventdata(NULL, 0);
 }
 
 void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
index 085964278b1b93527d7c7f92580d4e1e88187526..ccace09b1001e1f28123834a9c2da26e63b89958 100644 (file)
@@ -335,6 +335,10 @@ void ui_textarea_realize_event(GtkWidget *widget, gpointer data) {
 
 
 void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea) {
+    if(!ui_onchange_events_is_enabled()) {
+        return;
+    }
+    
     UiText *value = textarea->var->value;
     
     UiEvent e;
@@ -698,14 +702,18 @@ void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield) {
 }
 
 void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) {
-    UiString *value = textfield->var->value;
+    if(!ui_onchange_events_is_enabled()) {
+        return;
+    }
+    
+    UiString *value = textfield->var ? textfield->var->value : NULL;
     
     UiEvent e;
     e.obj = textfield->obj;
     e.window = e.obj->window;
     e.document = textfield->obj->ctx->document;
     e.eventdata = value;
-    e.eventdatatype = UI_EVENT_DATA_TEXT_VALUE;
+    e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0;
     e.intval = 0;
     e.set = ui_get_setop();
     
@@ -720,12 +728,14 @@ void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) {
 
 void ui_textfield_activate(GtkEntry* self, UiTextField *textfield) {
     if(textfield->onactivate) {
+        UiString *value = textfield->var ? textfield->var->value : NULL;
+        
         UiEvent e;
         e.obj = textfield->obj;
         e.window = e.obj->window;
         e.document = textfield->obj->ctx->document;
-        e.eventdata = NULL;
-        e.eventdatatype = 0;
+        e.eventdata = value;
+        e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0;
         e.intval = 0;
         e.set = ui_get_setop();
         textfield->onactivate(&e, textfield->onactivatedata);
index a286f8a5408007d3ca89684884556747eea84579..0b14370910682a584453abaa88707a802afda178 100644 (file)
@@ -75,12 +75,42 @@ static XtResource resources[] =
         (XtPointer) 0
     },
     {
-        gridMargin,
-        gridMargin,
+        gridPaddingLeft,
+        gridPaddingLeft,
         XmRDimension,
         sizeof (Dimension),
         XtOffsetOf( GridRec,
-                   mywidget.margin),
+                   mywidget.padding_left),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridPaddingRight,
+        gridPaddingRight,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridRec,
+                   mywidget.padding_right),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridPaddingTop,
+        gridPaddingTop,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridRec,
+                   mywidget.padding_top),
+        XmRImmediate,
+        (XtPointer) 0
+    },
+    {
+        gridPaddingBottom,
+        gridPaddingBottom,
+        XmRDimension,
+        sizeof (Dimension),
+        XtOffsetOf( GridRec,
+                   mywidget.padding_bottom),
         XmRImmediate,
         (XtPointer) 0
     }
@@ -406,8 +436,10 @@ void grid_place_children(Grid w) {
     GridDef *rows = calloc(nrows, sizeof(GridDef));
     int num_cols_expanding = 0;
     int num_rows_expanding = 0;
-    int req_width = 0;
-    int req_height = 0;
+    int req_width = w->mywidget.padding_left + w->mywidget.padding_right;
+    int req_height = w->mywidget.padding_top + w->mywidget.padding_bottom;
+    int width = w->core.width;
+    int height = w->core.height;
     
     //printf("container width: %d\n", (int)w->core.width);
     
@@ -428,6 +460,8 @@ void grid_place_children(Grid w) {
             if(constraints->grid.pref_width < constraints->grid.min_width) {
                 constraints->grid.pref_width = constraints->grid.min_width;
             }
+            int elm_width = constraints->grid.pref_width + constraints->grid.margin_left + constraints->grid.margin_right;
+            int elm_height = constraints->grid.pref_height + constraints->grid.margin_top + constraints->grid.margin_bottom;
             
             if(constraints->grid.colspan > span_max || constraints->grid.rowspan > span_max) {
                 continue;
@@ -489,12 +523,12 @@ void grid_place_children(Grid w) {
                     span_width = last_col->size;
                     
                 }
-                int diff = constraints->grid.pref_width - span_width;
+                int diff = elm_width - span_width;
                 if(diff > 0) {
                     last_col->size += diff; 
                 }
-            } else if(constraints->grid.pref_width > col->size) {
-                col->size = constraints->grid.pref_width;
+            } else if(elm_width > col->size) {
+                col->size = elm_width;
             }
             // row size
             if(constraints->grid.rowspan > 1) {
@@ -505,12 +539,12 @@ void grid_place_children(Grid w) {
                     span_height = last_row->size;
                     
                 }
-                int diff = constraints->grid.pref_height - span_height;
+                int diff = elm_height - span_height;
                 if(diff > 0) {
                     last_row->size += diff; 
                 }
-            } else if(constraints->grid.pref_height > row->size) {
-                row->size = constraints->grid.pref_height;
+            } else if(elm_height > row->size) {
+                row->size = elm_height;
             }
         }
         span_max = 50000; // not sure if this is unreasonable low or high
@@ -568,13 +602,13 @@ void grid_place_children(Grid w) {
     
     // how much space can we add to each expanding col/row
     int hexpand = 0;
-    int width_diff = (int)w->core.width - req_width;
+    int width_diff = 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;
+    int x = w->mywidget.padding_left;
     for(int i=0;i<ncols;i++) {
         cols[i].pos = x;
         if(cols[i].expand) {
@@ -586,13 +620,13 @@ void grid_place_children(Grid w) {
     }
     
     int vexpand = 0;
-    int height_diff = (int)w->core.height - req_height;
+    int height_diff = 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;
+    int y = w->mywidget.padding_bottom;
     for(int i=0;i<nrows;i++) {
         rows[i].pos = y;
         if(rows[i].expand) {
@@ -608,8 +642,8 @@ void grid_place_children(Grid w) {
         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 x = c.pos + constraints->grid.margin_left;
+        int y = r.pos + constraints->grid.margin_top;
         int width = constraints->grid.pref_width;
         int height = constraints->grid.pref_height;
         if(constraints->grid.hfill) {
@@ -622,7 +656,7 @@ void grid_place_children(Grid w) {
                 }
                 width = cwidth;
             } else {
-                width = c.size - w->mywidget.columnspacing;
+                width = c.size - w->mywidget.columnspacing - constraints->grid.margin_left - constraints->grid.margin_right;
             }
         }
         if(constraints->grid.vfill) {
@@ -635,10 +669,10 @@ void grid_place_children(Grid w) {
                 }
                 height = cheight;
             } else {
-                height = r.size - w->mywidget.rowspacing;
+                height = r.size - w->mywidget.rowspacing - constraints->grid.margin_top - constraints->grid.margin_bottom;
             }
         }
-        
+              
         if(width > 0 && height > 0) {
             XtConfigureWidget(child, x, y, width, height, child->core.border_width);
         }
index f135cbdcfb4e73ed8f5c62ab427c08805afbf6e8..b6f22deaf41dbc3c7e0c18820f9675211c0a4d1d 100644 (file)
@@ -42,8 +42,11 @@ extern "C" {
 
 // resources
 #define gridColumnSpacing "gridColumnSpacing"
-#define gridRowSpacing "gridRowSpacing"
-#define gridMargin "gridMargin"
+#define gridRowSpacing    "gridRowSpacing"
+#define gridPaddingLeft   "gridPaddingLeft"
+#define gridPaddingRight  "gridPaddingRight"
+#define gridPaddingTop    "gridPaddingTop"
+#define gridPaddingBottom "gridPaddingBottom"
 
 // constraints    
 #define gridColumn "gridColumn"
@@ -81,15 +84,14 @@ typedef struct GridClassRec {
 
 
 typedef struct GridPart {
-    int margin_left;
-    int margin_right;
-    int margin_top;
-    int margin_bottom;
+    int padding_left;
+    int padding_right;
+    int padding_top;
+    int padding_bottom;
     int max_col;
     int max_row;
     Dimension columnspacing;
     Dimension rowspacing;
-    Dimension margin;
     
     Boolean sizerequest;
 } GridPart;
index d2528c4c1541c196e4f8e59de05629ef7d56db08..6393e761d0edc7c5271e16234b8c1b32b8f1587e 100644 (file)
@@ -77,7 +77,7 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
        XtAddCallback(
                 button,
                 XmNdestroyCallback,
-                (XtCallbackProc)ui_destroy_eventdata,
+                (XtCallbackProc)ui_destroy_data,
                 eventdata);
     }
     
@@ -224,7 +224,7 @@ void ui_bind_togglebutton(
     XtAddCallback(
             widget,
             XmNdestroyCallback,
-            (XtCallbackProc)ui_destroy_eventdata,
+            (XtCallbackProc)ui_destroy_data,
             event);
 }
 
@@ -341,7 +341,7 @@ void ui_bind_radiobutton(UiObject *obj, Widget rbutton, UiInteger *value, const
     XtAddCallback(
             rbutton,
             XmNdestroyCallback,
-            (XtCallbackProc)ui_destroy_eventdata,
+            (XtCallbackProc)ui_destroy_data,
             event);
 }
 
@@ -411,7 +411,7 @@ UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) {
     XtAddCallback(
             button,
             XmNdestroyCallback,
-            (XtCallbackProc)ui_destroy_eventdata,
+            (XtCallbackProc)ui_destroy_data,
             event);
     
     XmStringFree(label);
index cce99b6960f0ed2aeef7112879035624110a7bc7..7213ed369935cc6add73c0f661882d26b19f493d 100644 (file)
@@ -55,6 +55,18 @@ void ui_container_add(UiContainerPrivate *container, Widget widget) {
     container->add(container, widget);
 }
 
+void ui_container_apply_grid_margin(
+        Arg *args,
+        int *n,
+        int margin_left, int margin_right, int margin_top, int margin_bottom)
+{
+    int c = *n;
+    XtSetArg(args[c], gridMarginLeft, margin_left); c++;
+    XtSetArg(args[c], gridMarginRight, margin_right); c++;
+    XtSetArg(args[c], gridMarginTop, margin_top); c++;
+    XtSetArg(args[c], gridMarginBottom, margin_bottom); c++;
+    *n = c;
+}
 
 /* ---------------------------- Box Container ---------------------------- */
 
@@ -102,7 +114,7 @@ UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orie
 }
 
 static Widget ui_box_container_prepare(UiBoxContainer *box, UiLayout *layout, Arg *args, int *n) {
-    int a = *n;
+    ui_container_apply_grid_margin(args, n, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
     box->n++;
     return box->container.widget;
 }
@@ -151,7 +163,6 @@ UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
     UiLayout layout = UI_ARGS2LAYOUT(args);
     
     Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
-    XtSetArg(xargs[n], gridMargin, args->margin); n++;
     XtSetArg(xargs[n], gridColumnSpacing, args->columnspacing); n++;
     XtSetArg(xargs[n], gridRowSpacing, args->rowspacing); n++;
     Widget grid = XtCreateManagedWidget(args->name ? args->name : "gridcontainer", gridClass, parent, xargs, n);
@@ -218,6 +229,7 @@ Widget ui_grid_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg
     }
     
     *n = a;
+    ui_container_apply_grid_margin(args, n, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
     return ctn->widget;
 }
 
@@ -239,7 +251,7 @@ UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
     Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
     
     char *name = args->name ? (char*)args->name : "frame";
-    Widget frame = XmCreateFrame(parent, name, xargs, 6);
+    Widget frame = XmCreateFrame(parent, name, xargs, n);
     XtManageChild(frame);
     ui_container_add(ctn, frame);
     
@@ -259,7 +271,8 @@ UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
     UiContainerArgs sub_args = {
         .spacing = args->spacing,
         .columnspacing = args->columnspacing,
-        .rowspacing = args->rowspacing
+        .rowspacing = args->rowspacing,
+        .margin = args->padding
     };
     switch(args->subcontainer) {
         default: break;
@@ -334,9 +347,10 @@ static void ui_tabbar_resize(Widget widget, XtPointer udata, XtPointer cdata) {
     if(numbuttons == 0) {
         return;
     }
+    width--;
     int button_width = width / numbuttons;
     int x = 0;
-    
+        
     CxIterator i = cxListIterator(tabview->tabs);
     cx_foreach(UiTab *, tab, i) {
         if(i.index + 1 == numbuttons) {
@@ -432,6 +446,7 @@ UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) {
     tabview->current_index = -1;
     
     UiTabViewContainer *ct = ui_malloc(obj->ctx, sizeof(UiTabViewContainer));
+    memset(ct, 0, sizeof(UiTabViewContainer));
     ct->container.widget = form;
     ct->container.type = UI_CONTAINER_TABVIEW;
     ct->container.prepare = ui_tabview_container_prepare;
index e6fe42203a6e5031ab5bac1ec280769905866e8c..30262e5dee5c23298d62411a39d71f748fef4b9d 100644 (file)
@@ -129,6 +129,10 @@ typedef struct UiTabViewContainer {
 
 Widget ui_container_prepare(UiContainerPrivate *container, UiLayout *layout, Arg *args, int *n);
 void ui_container_add(UiContainerPrivate *container, Widget widget);
+void ui_container_apply_grid_margin(
+        Arg *args,
+        int *n,
+        int margin_left, int margin_right, int margin_top, int margin_bottom);
 
 void ui_motif_tabview_select(UiMotifTabView *tabview, int tab);
 void ui_motif_tabview_add_tab(UiMotifTabView *tabview, int index, const char *name, Widget child);
diff --git a/ui/motif/entry.c b/ui/motif/entry.c
new file mode 100644 (file)
index 0000000..d21a400
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 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 "entry.h"
+
+
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
+    Arg xargs[16];
+    int n = 0;
+    
+    double min = args->min;
+    double max = args->max != 0 ? args->max : 1000;
+    
+    UiVar *var = NULL;
+    UiVarType vartype = 0;
+    if(args->varname) {
+        var = uic_get_var(obj->ctx, args->varname);
+        if(var) {
+            vartype = var->type;
+        } else {
+            var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, args->varname, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
+        }
+    }
+    
+    if(!var) {
+        if(args->intvalue) {
+            var = uic_widget_var(obj->ctx, obj->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+            vartype = UI_VAR_INTEGER;
+        } else if(args->doublevalue) {
+            var = uic_widget_var(obj->ctx, obj->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+            vartype = UI_VAR_DOUBLE;
+        } else if(args->rangevalue) {
+            var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
+        }
+    }
+    
+    if(vartype == UI_VAR_RANGE) {
+        UiRange *r = var->value;
+        min = r->min;
+        max = r->max;
+    }
+    if(args->step == 0) {
+        args->step = 1;
+    }
+    
+    UiContainerPrivate *ctn = ui_obj_container(obj);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+
+    
+    XtSetArg(xargs[n], XmNminimumValue, 0); n++;
+    XtSetArg(xargs[n], XmNmaximumValue, 100); n++;
+    XtSetArg(xargs[n], XmNincrementValue, 1); n++;
+    XtSetArg(xargs[n], XmNspinBoxChildType, XmNUMERIC); n++;
+    
+    Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
+    
+    char *name = args->name ? (char*)args->name : "button";
+    Widget spinbox = XmCreateSimpleSpinBox(parent, name, xargs, n);
+    XtManageChild(spinbox);
+    ui_container_add(ctn, spinbox);
+    
+    ui_set_widget_groups(obj->ctx, spinbox, args->groups);
+    
+    WidgetList children;
+    Cardinal num_children;
+    unsigned char type;
+    
+    Widget textfield = NULL;
+    XtVaGetValues(
+            spinbox,
+            XmNchildren, &children,
+            XmNnumChildren, &num_children,
+            NULL);
+
+    for(int i = 0;i<num_children;i++) {
+        XtVaGetValues(children[i], XmNspinBoxChildType, &type, NULL);
+        Widget w = children[i];
+        if(type == XmNUMERIC) {
+            textfield = children[i];
+        }
+    }
+     
+    UiSpinBox *data = malloc(sizeof(UiSpinBox));
+    data->obj = obj;
+    data->textfield = textfield;
+    data->var = var;
+    data->vartype = vartype;
+    data->obs = NULL;
+    data->onchange = args->onchange;
+    data->onchangedata = args->onchangedata;
+    data->value = 0;
+    data->min = min;
+    data->max = max;
+    data->increment = args->step;
+    data->digits = args->digits;
+    
+    UiObserver **obs = NULL;
+    if(var) {
+        double value = 0;
+        switch(vartype) {
+            default: break;
+            case UI_VAR_INTEGER: {
+                UiInteger *i = var->value;
+                i->get = ui_spinbutton_getint;
+                i->set = ui_spinbutton_setint;
+                i->obj = data;
+                value = (double)i->value;
+                obs = &i->observers;
+                break;
+            }
+            case UI_VAR_DOUBLE: {
+                UiDouble *d = var->value;
+                d->get = ui_spinbutton_getdouble;
+                d->set = ui_spinbutton_setdouble;
+                d->obj = data;
+                value = d->value;
+                obs = &d->observers;
+                break;
+            }
+            case UI_VAR_RANGE: {
+                UiRange *r = var->value;
+                r->get = ui_spinbutton_getrangeval;
+                r->set = ui_spinbutton_setrangeval;
+                r->setrange = ui_spinbutton_setrange;
+                r->setextent = ui_spinbutton_setextent;
+                r->obj = data;
+                value = r->value;
+                obs = &r->observers;
+                break;
+            }
+        }
+        ui_spinbox_set_value(data, value);
+    }
+    data->obs = obs;
+    
+    XtAddCallback(
+            spinbox,
+            XmNvalueChangedCallback,
+            (XtCallbackProc)ui_spinbox_value_changed,
+            data);
+    
+    XtAddCallback(
+            spinbox,
+            XmNdestroyCallback,
+            (XtCallbackProc)ui_destroy_data,
+            data);
+    
+    XmTextFieldSetString(textfield, "0");
+    
+    
+    return spinbox;
+}
+
+void ui_spinbox_set_value(UiSpinBox *spinbox, double value) {
+    if(value < spinbox->min) {
+        value = spinbox->min;
+    }
+    if(value > spinbox->max) {
+        value = spinbox->max;
+    }
+    
+    char buf[32];
+    snprintf(buf, 32, "%.*f", spinbox->digits, spinbox->value);
+    XmTextFieldSetString(spinbox->textfield, buf);
+    spinbox->value = value;
+}
+
+void ui_spinbox_value_changed(Widget widget, UiSpinBox *spinbox, XmSpinBoxCallbackStruct *cb) {
+    Boolean update_value = TRUE;
+    double value = spinbox->value;
+    switch(cb->reason) {
+        case XmCR_OK: {
+            update_value = FALSE;
+            break;
+        }
+        case XmCR_SPIN_NEXT: {
+            value += spinbox->increment;
+            break;
+        }
+        case XmCR_SPIN_PRIOR: {
+            value -= spinbox->increment;
+            break;
+        }
+    }
+    
+    if(update_value) {
+        ui_spinbox_set_value(spinbox, value);
+        
+        UiEvent event;
+        event.obj = spinbox->obj;
+        event.window = event.obj->window;
+        event.document = event.obj->ctx->document;
+        event.eventdata = NULL;
+        event.eventdatatype = 0;
+        event.intval = (int64_t)value;
+        event.set = ui_get_setop();
+        
+        if(spinbox->onchange) {
+            spinbox->onchange(&event, spinbox->onchangedata);
+        }
+
+        UiObserver *obs = *spinbox->obs;
+        ui_notify_evt(*spinbox->obs, &event);
+    }
+}
+
+int64_t ui_spinbutton_getint(UiInteger *i) {
+    UiSpinBox *spinbox = i->obj;
+    i->value = (int64_t)spinbox->value;
+    return i->value;
+}
+
+void ui_spinbutton_setint(UiInteger *i, int64_t val) {
+    UiSpinBox *spinbox = i->obj;
+    ui_spinbox_set_value(spinbox, (double)val);
+    i->value = spinbox->value;
+}
+
+double ui_spinbutton_getdouble(UiDouble *d) {
+    UiSpinBox *spinbox = d->obj;
+    d->value = spinbox->value;
+    return d->value;
+}
+
+void ui_spinbutton_setdouble(UiDouble *d, double val) {
+    UiSpinBox *spinbox = d->obj;
+    ui_spinbox_set_value(spinbox, val);
+    d->value = spinbox->value;
+}
+
+double ui_spinbutton_getrangeval(UiRange *r) {
+    UiSpinBox *spinbox = r->obj;
+    r->value = spinbox->value;
+    return r->value;
+}
+
+void ui_spinbutton_setrangeval(UiRange *r, double val) {
+    UiSpinBox *spinbox = r->obj;
+    ui_spinbox_set_value(spinbox, val);
+    r->value = spinbox->value;
+}
+void ui_spinbutton_setrange(UiRange *r, double min, double max) {
+    UiSpinBox *spinbox = r->obj;
+    spinbox->min = min;
+    spinbox->max = max;
+    r->min = min;
+    r->max = max;
+}
+
+void ui_spinbutton_setextent(UiRange *r, double extent) {
+    UiSpinBox *spinbox = r->obj;
+    spinbox->increment = extent;
+    r->extent = extent;
+}
diff --git a/ui/motif/entry.h b/ui/motif/entry.h
new file mode 100644 (file)
index 0000000..ba47f4e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 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 ENTRY_H
+#define ENTRY_H
+
+#include "../ui/entry.h"
+#include "container.h"
+#include "toolkit.h"
+
+#include <Xm/SSpinB.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UiSpinBox {
+    UiObject *obj;
+    Widget textfield;
+    UiVar *var;
+    UiVarType vartype;
+    UiObserver **obs;
+    ui_callback onchange;
+    void* onchangedata;
+    double value;
+    double min;
+    double max;
+    double increment;
+    int digits;
+} UiSpinBox;
+
+void ui_spinbox_set_value(UiSpinBox *spinbox, double value);
+    
+void ui_spinbox_value_changed(Widget widget, UiSpinBox *spinbox, XmSpinBoxCallbackStruct *cb);
+
+int64_t ui_spinbutton_getint(UiInteger *i);
+void ui_spinbutton_setint(UiInteger *i, int64_t val);
+
+double ui_spinbutton_getdouble(UiDouble *d);
+void ui_spinbutton_setdouble(UiDouble *d, double val);
+
+double ui_spinbutton_getrangeval(UiRange *r);
+void ui_spinbutton_setrangeval(UiRange *r, double val);
+void ui_spinbutton_setrange(UiRange *r, double min, double max);
+void ui_spinbutton_setextent(UiRange *r, double extent);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ENTRY_H */
+
index 48be0034e0dcc834abc5162507cea062fd7f4dc2..2acb2c30855d826683a7b0b45aa10acb316ebe46 100644 (file)
@@ -110,15 +110,15 @@ char* ui_label_get(UiString *s) {
 }
 
 void ui_label_set(UiString *s, const char *str) {
+    Widget w = s->obj;
+    XmString s1 = XmStringCreateLocalized(str ? (char*)str : "");
+    XtVaSetValues(w, XmNlabelString, s1, NULL);
+    XmStringFree(s1);
     if(s->value.free) {
         s->value.free(s->value.ptr);
         s->value.free = NULL;
         s->value.ptr = NULL;
     }
-    Widget w = s->obj;
-    XmString s1 = XmStringCreateLocalized(str ? (char*)str : "");
-    XtVaSetValues(w, XmNlabelString, s1, NULL);
-    XmStringFree(s1);
 }
 
 /* -------------------------- progressbar/spiner -------------------------- */
index 0bb1cbe143bbc1063eb4122e7242a62420fe107a..b1e96eeac493e57270ff3dd0e40d3b8c1392889c 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <Xm/Xm.h>
 
 #include "container.h"
 
@@ -62,6 +63,9 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
     } else {
         XtSetArg(xargs[n], XmNselectionPolicy, XmSINGLE_SELECT); n++;
     }
+    if(args->height > 0) {
+        XtSetArg(xargs[n], XmNheight, args->height); n++;
+    }
     
     char *name = args->name ? (char*)args->name : "listview";
     Widget parent = ui_container_prepare(ctn, &layout, xargs, &n);
@@ -144,8 +148,12 @@ static void listview_save_selection(UiListView *listview, XmListCallbackStruct *
     UiListSelection sel = { cb->selected_item_count, NULL };
     if(sel.count > 0) {
         sel.rows = calloc(sel.count, sizeof(int));
-        for(int i=0;i<sel.count;i++) {
-            sel.rows[i] = cb->selected_item_positions[i]-1;
+        if(sel.count == 1) {
+            sel.rows[0] = cb->item_position-1;
+        } else if(cb->selected_item_positions) {
+            for(int i=0;i<sel.count;i++) {
+                sel.rows[i] = cb->selected_item_positions[i]-1;
+            }
         }
     }
     free(listview->current_selection.rows);
@@ -209,10 +217,18 @@ void ui_listview_update(UiList *list, int i) {
 
 UiListSelection ui_listview_getselection(UiList *list) {
     UiListView *listview = list->obj;
-    UiListSelection sel = { listview->current_selection.count, NULL };
-    if(sel.count > 0) {
-        sel.rows = calloc(sel.count, sizeof(int));
-        memcpy(sel.rows, listview->current_selection.rows, sel.count*sizeof(int));
+    UiListSelection sel = { 0, NULL };
+    int *selpositions = NULL;
+    int numpos = 0;
+    XtVaGetValues(listview->widget, XmNselectedPositions, &selpositions, XmNselectedPositionCount, &numpos, NULL);
+    if(numpos > 0) {
+        sel.rows = calloc(numpos, sizeof(int));
+        sel.count = numpos;
+        memcpy(sel.rows, selpositions, numpos*sizeof(int));
+        // motif selected positions start at index 1 -> translate positions
+        for(int i=0;i<numpos;i++) {
+            sel.rows[i]--;
+        }
     }
     return sel;
 }
@@ -299,8 +315,8 @@ UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
         UiList *list = var->value;
         list->obj = listview;
         list->update = ui_listview_update;
-        list->getselection = ui_listview_getselection;
-        list->setselection = ui_listview_setselection;
+        list->getselection = ui_dropdown_getselection;
+        list->setselection = ui_dropdown_setselection;
         ui_listview_update(list, 0);
     }
     
@@ -317,3 +333,25 @@ UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
     
     return widget;
 }
+
+void ui_dropdown_setselection(UiList *list, UiListSelection selection) {
+    UiListView *listview = list->obj;
+    if(selection.count > 0) {
+        XtVaSetValues(listview->widget, XmNselectedPosition, selection.rows[0], NULL);
+    } else {
+        XtVaSetValues(listview->widget, XmNselectedPosition, 0, NULL);
+    }
+}
+
+UiListSelection ui_dropdown_getselection(UiList *list) {
+    UiListView *listview = list->obj;
+    int pos = -1;
+    XtVaGetValues(listview->widget, XmNselectedPosition, &pos, NULL);
+    UiListSelection sel = { 0, NULL };
+    if(pos >= 0) {
+        sel.rows = malloc(sizeof(int));
+        sel.rows[0] = pos;
+        sel.count = 1;
+    }
+    return sel;
+}
index 2d88c72b884651a9bc11fb20a0b58a46a5d67c59..04756a2140239a7f86fff469ff77e55f3a7aea7d 100644 (file)
@@ -69,6 +69,9 @@ void ui_listview_update(UiList *list, int i);
 UiListSelection ui_listview_getselection(UiList *list);
 void ui_listview_setselection(UiList *list, UiListSelection selection);
 
+void ui_dropdown_setselection(UiList *list, UiListSelection selection);
+UiListSelection ui_dropdown_getselection(UiList *list);
+
 void* ui_strmodel_getvalue(void *elm, int column);
 
 #ifdef __cplusplus
index c2038a84cf1a27297d694703a17df7dc72cc7717..345ef054a2a305da0cdf68a34cd6b17fb91dc119 100644 (file)
@@ -93,16 +93,15 @@ void add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) {
         XtSetArg(args[n], XmNlabelString, s); n++;
     }
     
-    Widget submenu = XmVaCreateSimplePulldownMenu(parent, "menu_pulldown", i, NULL, NULL);
+    Widget submenu = XmCreatePulldownMenu(parent, "menu_pulldown", NULL, 0);
     XtSetArg(args[n], XmNsubMenuId, submenu); n++;
-    Widget menuItem = XtCreateManagedWidget(
+    (void)XtCreateManagedWidget(
             "menuitem",
             xmCascadeButtonWidgetClass,
             parent,
             args,
             n);
     
-    
     if(s) {
         XmStringFree(s);
     }
@@ -145,7 +144,7 @@ void add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj)
        XtAddCallback(
                 mitem,
                 XmNdestroyCallback,
-                (XtCallbackProc)ui_destroy_eventdata,
+                (XtCallbackProc)ui_destroy_data,
                 eventdata);
     }
     
@@ -337,7 +336,7 @@ void ui_update_menuitem_list(UiActiveMenuItemList *list) {
             XtAddCallback(
                     mitem,
                     XmNdestroyCallback,
-                    (XtCallbackProc)ui_destroy_eventdata,
+                    (XtCallbackProc)ui_destroy_data,
                     eventdata);
         }
         
index b1b51b21f4aeafedffbd205c5fe3cbb0391bce9a..2a47e14b26688a17b7c09187457b610439c32601 100644 (file)
@@ -45,6 +45,7 @@ MOTIFOBJ += range.o
 MOTIFOBJ += dnd.o
 MOTIFOBJ += image.o
 MOTIFOBJ += Grid.o
+MOTIFOBJ += entry.o
 
 TOOLKITOBJS += $(MOTIFOBJ:%=$(MOTIF_OBJPRE)%)
 TOOLKITSOURCE += $(MOTIFOBJ:%.o=motif/%.c)
index 5c5db1f39215680a5a693257ce23d592d5806ced..00d5702fff21b4dfd0ed14b19a130456cb9962a4 100644 (file)
@@ -407,8 +407,18 @@ static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs *args, int frame
     
     ui_set_widget_groups(obj->ctx, textfield, args->groups);
     
+    UiEventDataExt *eventdata = malloc(sizeof(UiEventDataExt));
+    memset(eventdata, 0, sizeof(UiEventDataExt));
+    eventdata->obj = obj;
+    eventdata->callback = args->onactivate;
+    eventdata->userdata = args->onactivatedata;
+    eventdata->callback2 = args->onchange;
+    eventdata->userdata2 = args->onchangedata;
+    
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
     if(var) {
+        eventdata->customdata0 = var;
+        
         UiString *value = (UiString*)var->value;
         value->obj = textfield;
         value->get = ui_textfield_get;
@@ -419,9 +429,54 @@ static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs *args, int frame
         }
     }
     
+    XtAddCallback(
+            textfield,
+            XmNactivateCallback,
+            (XtCallbackProc)ui_textfield_activate,
+            eventdata);
+    XtAddCallback(
+            textfield,
+            XmNvalueChangedCallback,
+            (XtCallbackProc)ui_textfield_value_changed,
+            eventdata);
+    XtAddCallback(
+            textfield,
+            XmNdestroyCallback,
+            (XtCallbackProc)ui_destroy_data,
+            eventdata);
+    
     return textfield;
 }
 
+static void textfield_event(UiEventDataExt *eventdata, ui_callback callback, void *userdata) {
+    if(callback) {
+        UiVar *var = eventdata->customdata0;
+        UiString *value = var ? var->value : NULL;
+        
+        UiEvent e;
+        e.obj = eventdata->obj;
+        e.window = e.obj->window;
+        e.document = e.obj->ctx->document;
+        e.eventdata = value;
+        e.eventdatatype = value ? UI_EVENT_DATA_TEXT_VALUE : 0;
+        e.intval = 0;
+        e.set = ui_get_setop();
+        callback(&e, userdata);
+    }
+}
+
+void ui_textfield_activate(Widget widget, XtPointer ud, XtPointer cb) {
+    UiEventDataExt *eventdata = ud;
+    textfield_event(ud, eventdata->callback, eventdata->userdata);
+}
+
+void ui_textfield_value_changed(Widget widget, XtPointer ud, XtPointer cb) {
+    UiEventDataExt *eventdata = ud;
+    if(ui_onchange_events_is_enabled()) {
+        textfield_event(ud, eventdata->callback2, eventdata->userdata2);
+    }
+}
+
 UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs *args) {
     return create_textfield(obj, args, FALSE, FALSE);
 }
@@ -1018,7 +1073,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
         XtAddCallback(
                 pathbar->widget,
                 XmNdestroyCallback,
-                (XtCallbackProc)ui_destroy_eventdata,
+                (XtCallbackProc)ui_destroy_data,
                 eventdata);
     }
     
index a7e4f8572cad3f0eea41899c25aed8b266b03a7a..9f2932e3486c701b339c756491f65d0eef5765f7 100644 (file)
@@ -86,6 +86,9 @@ 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);
 
+void ui_textfield_activate(Widget widget, XtPointer ud, XtPointer cb);
+void ui_textfield_value_changed(Widget widget, XtPointer ud, XtPointer cb);
+
 char* ui_textfield_get(UiString *str);
 void ui_textfield_set(UiString *str, const char *value);
 
index 1b432c6b5fff64bb51f72a534370175444f114eb..7e14250ce4e02827b26548d77d12c6194bc1f268 100644 (file)
@@ -325,7 +325,7 @@ void ui_window_dark_theme(Display *dp, Window window) {
             4);
 }
 
-void ui_destroy_eventdata(Widget w, XtPointer data, XtPointer d) {
+void ui_destroy_data(Widget w, XtPointer data, XtPointer d) {
     free(data);
 }
 
index 5f3d4405f44069ea674bdc51901b8cf42cd55328..12cc703c509d75bbb47fc48366865283eb208394 100644 (file)
@@ -93,7 +93,7 @@ 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_destroy_data(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);
index 83cd78abc0df9becf116d93fd1634a9167f7d018..80175dc9dd5a4fdc4e1e76b6813fa47adcdb3546 100644 (file)
@@ -417,6 +417,7 @@ struct UiGeneric {
 };
     
 typedef void (*ui_list_init_func)(UiContext *ctx, UiList *list, void *userdata);
+typedef void (*ui_list_destroy_func)(UiContext *ctx, UiList *list, void *userdata);
 
 /*
  * abstract list
@@ -635,7 +636,7 @@ UIEXPORT void ui_notify_evt(UiObserver *observer, UiEvent *event);
 
 UIEXPORT UiList* ui_list_new(UiContext *ctx, const char *name);
 UIEXPORT UiList* ui_list_new2(UiContext *ctx, const char *name, ui_list_init_func init, void *userdata);
-UIEXPORT void ui_list_free(UiList *list);
+UIEXPORT void ui_list_free(UiContext *ctx, UiList *list);
 UIEXPORT void* ui_list_first(UiList *list);
 UIEXPORT void* ui_list_next(UiList *list);
 UIEXPORT void* ui_list_get(UiList *list, int i);
@@ -680,9 +681,12 @@ UIEXPORT void ui_condvar_destroy(UiCondVar *var);
 
 UIEXPORT void ui_setop_enable(int set);
 UIEXPORT int ui_get_setop(void);
+UIEXPORT void ui_onchange_events_enable(UiBool enable);
+UIEXPORT UiBool ui_onchange_events_is_enabled(void);
 
     
 UIEXPORT void ui_global_list_initializer(ui_list_init_func func, void *userdata);
+UIEXPORT void ui_global_list_destructor(ui_list_destroy_func func, void *userdata);
 UIEXPORT void ui_list_class_set_first(UiList *list, void*(*first)(UiList *list));
 UIEXPORT void ui_list_class_set_next(UiList *list, void*(*next)(UiList *list));
 UIEXPORT void ui_list_class_set_get(UiList *list, void*(*get)(UiList *list, int i));