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