From: Olaf Wintermann Date: Sun, 9 Nov 2025 22:41:53 +0000 (+0100) Subject: update window layout X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;p=mizunara.git update window layout --- diff --git a/libidav/pwdstore.c b/libidav/pwdstore.c index ba97c09..c2089b9 100644 --- a/libidav/pwdstore.c +++ b/libidav/pwdstore.c @@ -263,7 +263,7 @@ static int read_pwdentry(PwdStore *p, CxBuffer *in) { } static void remove_list_entries(PwdStore *s, const char *id) { - CxIterator i = cxListMutIterator(s->locations); + CxIterator i = cxListIterator(s->locations); cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { cxIteratorFlagRemoval(i); @@ -271,7 +271,7 @@ static void remove_list_entries(PwdStore *s, const char *id) { break; } } - i = cxListMutIterator(s->noloc); + i = cxListIterator(s->noloc); cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { cxIteratorFlagRemoval(i); diff --git a/libidav/resource.c b/libidav/resource.c index 19ddffa..fa96581 100644 --- a/libidav/resource.c +++ b/libidav/resource.c @@ -1599,7 +1599,7 @@ CxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) { property->value = n->children ? dav_convert_xml(sn, n->children) : NULL; cxmutstr propkey = dav_property_key(property->ns->name, property->name); - cxMapPut(map, cx_hash_key_cxstr(propkey), property); + cxMapPut(map, propkey, property); cx_strfree(&propkey); } n = n->next; diff --git a/mizunara/Makefile b/mizunara/Makefile index 4fb25c1..0722cf4 100644 --- a/mizunara/Makefile +++ b/mizunara/Makefile @@ -47,7 +47,7 @@ OBJ = $(SRC:%.c=$(BUILD_ROOT)/build/mizunara/%.$(OBJ_EXT)) all: $(BUILD_ROOT)/build/bin/mizunara $(BUILD_ROOT)/build/bin/mizunara: $(OBJ) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)uitk$(LIB_EXT) - $(CC) -o $(BUILD_ROOT)/build/bin/mizunara$(APP_EXT) $(OBJ) -L$(BUILD_ROOT)/build/lib -lidav $(LDFLAGS) $(TK_LDFLAGS) $(DAV_LDFLAGS) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/libucx.a $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/libuitk.a + $(CC) -o $(BUILD_ROOT)/build/bin/mizunara$(APP_EXT) $(OBJ) -L$(BUILD_ROOT)/build/lib -lidav $(LDFLAGS) $(TK_LDFLAGS) $(DAV_LDFLAGS) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/libuitk.a $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/libucx.a $(BUILD_ROOT)/build/mizunara/%.$(OBJ_EXT): %.c $(CC) $(CFLAGS) $(TK_CFLAGS) -o $@ -c $< diff --git a/mizunara/window.c b/mizunara/window.c index e004583..d3edb43 100644 --- a/mizunara/window.c +++ b/mizunara/window.c @@ -89,7 +89,7 @@ UiObject* window_create(const char *url) { .fill = TRUE); } - ui_hbox(obj, .spacing = 4) { + ui_hbox(obj, .spacing = 4, .margin_left = 6, .margin_right = 8) { ui_button(obj, .style_class = "flat", .icon = UI_ICON_GO_BACK); ui_button(obj, .style_class = "flat", .icon = UI_ICON_GO_FORWARD); ui_path_textfield(obj, .varname = "path", .fill = TRUE, .onactivate = action_pathbar_activate); diff --git a/ucx/Makefile b/ucx/Makefile index 1005259..4b61a56 100644 --- a/ucx/Makefile +++ b/ucx/Makefile @@ -38,6 +38,7 @@ SRC += hash_key.c SRC += hash_map.c SRC += iterator.c SRC += linked_list.c +SRC += kv_list.c SRC += list.c SRC += map.c SRC += printf.c diff --git a/ucx/allocator.c b/ucx/allocator.c index b10096c..323b14b 100644 --- a/ucx/allocator.c +++ b/ucx/allocator.c @@ -73,7 +73,7 @@ struct cx_allocator_s cx_stdlib_allocator = { NULL }; const CxAllocator * const cxStdlibAllocator = &cx_stdlib_allocator; -const CxAllocator * cxDefaultAllocator = cxStdlibAllocator; +const CxAllocator * cxDefaultAllocator = &cx_stdlib_allocator; int cx_reallocate_( void **mem, diff --git a/ucx/array_list.c b/ucx/array_list.c index a66db51..03c0bfe 100644 --- a/ucx/array_list.c +++ b/ucx/array_list.c @@ -50,7 +50,7 @@ static void *cx_array_default_realloc( } CxArrayReallocator cx_array_default_reallocator_impl = { - cx_array_default_realloc, NULL, NULL, 0, 0 + cx_array_default_realloc, NULL, NULL }; CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator_impl; @@ -72,11 +72,11 @@ static void *cx_array_advanced_realloc( } // retrieve the pointer to the actual allocator - const CxAllocator *al = alloc->ptr1; + const CxAllocator *al = alloc->allocator; // check if the array is still located on the stack void *newmem; - if (array == alloc->ptr2) { + if (array == alloc->stack_ptr) { newmem = cxMalloc(al, n); if (newmem != NULL && array != NULL) { memcpy(newmem, array, old_capacity*elem_size); @@ -89,15 +89,14 @@ static void *cx_array_advanced_realloc( struct cx_array_reallocator_s cx_array_reallocator( const struct cx_allocator_s *allocator, - const void *stackmem + const void *stack_ptr ) { if (allocator == NULL) { allocator = cxDefaultAllocator; } return (struct cx_array_reallocator_s) { cx_array_advanced_realloc, - (void*) allocator, (void*) stackmem, - 0, 0 + allocator, stack_ptr, }; } @@ -291,7 +290,7 @@ int cx_array_copy( *target, oldcap, newcap, elem_size, reallocator ); if (newmem == NULL) { - return 1; + return 1; // LCOV_EXCL_LINE } // repair src pointer, if necessary @@ -335,7 +334,7 @@ int cx_array_copy( return 0; } -int cx_array_insert_sorted( +static int cx_array_insert_sorted_impl( void **target, size_t *size, size_t *capacity, @@ -343,7 +342,8 @@ int cx_array_insert_sorted( const void *sorted_data, size_t elem_size, size_t elem_count, - CxArrayReallocator *reallocator + CxArrayReallocator *reallocator, + bool allow_duplicates ) { // assert pointers assert(target != NULL); @@ -369,6 +369,7 @@ int cx_array_insert_sorted( // store some counts size_t old_size = *size; size_t old_capacity = *capacity; + // the necessary capacity is the worst case assumption, including duplicates size_t needed_capacity = old_size + elem_count; // if we need more than we have, try a reallocation @@ -418,13 +419,60 @@ int cx_array_insert_sorted( bptr, cmp_func ); + // binary search gives us the smallest index; + // we also want to include equal elements here + while (si + copy_len < elem_count + && cmp_func(bptr, src+copy_len*elem_size) == 0) { + copy_len++; + } // copy the source elements - bytes_copied = copy_len * elem_size; - memcpy(dest, src, bytes_copied); - dest += bytes_copied; - src += bytes_copied; - si += copy_len; + if (copy_len > 0) { + if (allow_duplicates) { + // we can copy the entire chunk + bytes_copied = copy_len * elem_size; + memcpy(dest, src, bytes_copied); + dest += bytes_copied; + src += bytes_copied; + si += copy_len; + di += copy_len; + } else { + // first, check the end of the source chunk + // for being a duplicate of the bptr + const char *end_of_src = src + (copy_len - 1) * elem_size; + size_t skip_len = 0; + while (copy_len > 0 && cmp_func(bptr, end_of_src) == 0) { + end_of_src -= elem_size; + skip_len++; + copy_len--; + } + char *last = dest == *target ? NULL : dest - elem_size; + // then iterate through the source chunk + // and skip all duplicates with the last element in the array + size_t more_skipped = 0; + for (unsigned j = 0; j < copy_len; j++) { + if (last != NULL && cmp_func(last, src) == 0) { + // duplicate - skip + src += elem_size; + si++; + more_skipped++; + } else { + memcpy(dest, src, elem_size); + src += elem_size; + last = dest; + dest += elem_size; + si++; + di++; + } + } + // skip the previously identified elements as well + src += skip_len * elem_size; + si += skip_len; + skip_len += more_skipped; + // reduce the actual size by the number of skipped elements + *size -= skip_len; + } + } // when all source elements are in place, we are done if (si >= elem_count) break; @@ -443,20 +491,103 @@ int cx_array_insert_sorted( memmove(dest, bptr, bytes_copied); dest += bytes_copied; bptr += bytes_copied; + di += copy_len; bi += copy_len; } - // still source elements left? simply append them + // still source elements left? if (si < elem_count) { - memcpy(dest, src, elem_size * (elem_count - si)); + if (allow_duplicates) { + // duplicates allowed or nothing inserted yet: simply copy everything + memcpy(dest, src, elem_size * (elem_count - si)); + } else { + if (dest != *target) { + // skip all source elements that equal the last element + char *last = dest - elem_size; + while (si < elem_count) { + if (last != NULL && cmp_func(last, src) == 0) { + src += elem_size; + si++; + (*size)--; + } else { + break; + } + } + } + // we must check the elements in the chunk one by one + while (si < elem_count) { + // find a chain of elements that can be copied + size_t copy_len = 1, skip_len = 0; + { + const char *left_src = src; + while (si + copy_len < elem_count) { + const char *right_src = left_src + elem_size; + int d = cmp_func(left_src, right_src); + if (d < 0) { + if (skip_len > 0) { + // new larger element found; + // handle it in the next cycle + break; + } + left_src += elem_size; + copy_len++; + } else if (d == 0) { + left_src += elem_size; + skip_len++; + } else { + break; + } + } + } + size_t bytes_copied = copy_len * elem_size; + memcpy(dest, src, bytes_copied); + dest += bytes_copied; + src += bytes_copied + skip_len * elem_size; + si += copy_len + skip_len; + di += copy_len; + *size -= skip_len; + } + } } - // still buffer elements left? - // don't worry, we already moved them to the correct place + // buffered elements need to be moved when we skipped duplicates + size_t total_skipped = new_size - *size; + if (bi < new_size && total_skipped > 0) { + // move the remaining buffer to the end of the array + memmove(dest, bptr, elem_size * (new_size - bi)); + } return 0; } +int cx_array_insert_sorted( + void **target, + size_t *size, + size_t *capacity, + cx_compare_func cmp_func, + const void *sorted_data, + size_t elem_size, + size_t elem_count, + CxArrayReallocator *reallocator +) { + return cx_array_insert_sorted_impl(target, size, capacity, + cmp_func, sorted_data, elem_size, elem_count, reallocator, true); +} + +int cx_array_insert_unique( + void **target, + size_t *size, + size_t *capacity, + cx_compare_func cmp_func, + const void *sorted_data, + size_t elem_size, + size_t elem_count, + CxArrayReallocator *reallocator +) { + return cx_array_insert_sorted_impl(target, size, capacity, + cmp_func, sorted_data, elem_size, elem_count, reallocator, false); +} + size_t cx_array_binary_search_inf( const void *arr, size_t size, @@ -502,6 +633,13 @@ size_t cx_array_binary_search_inf( result = cmp_func(elem, arr_elem); if (result == 0) { // found it! + // check previous elements; + // when they are equal, report the smallest index + arr_elem -= elem_size; + while (pivot_index > 0 && cmp_func(elem, arr_elem) == 0) { + pivot_index--; + arr_elem -= elem_size; + } return pivot_index; } else if (result < 0) { // element is smaller than pivot, continue search left @@ -700,6 +838,31 @@ static size_t cx_arl_insert_sorted( } } +static size_t cx_arl_insert_unique( + struct cx_list_s *list, + const void *sorted_data, + size_t n +) { + // get a correctly typed pointer to the list + cx_array_list *arl = (cx_array_list *) list; + + if (cx_array_insert_unique( + &arl->data, + &list->collection.size, + &arl->capacity, + list->collection.cmpfunc, + sorted_data, + list->collection.elem_size, + n, + &arl->reallocator + )) { + // array list implementation is "all or nothing" + return 0; + } else { + return n; + } +} + static void *cx_arl_insert_element( struct cx_list_s *list, size_t index, @@ -717,7 +880,7 @@ static int cx_arl_insert_iter( const void *elem, int prepend ) { - struct cx_list_s *list = iter->src_handle.m; + struct cx_list_s *list = iter->src_handle; if (iter->index < list->collection.size) { if (cx_arl_insert_element(list, iter->index + 1 - prepend, elem) == NULL) { @@ -928,7 +1091,7 @@ static void cx_arl_reverse(struct cx_list_s *list) { static bool cx_arl_iter_valid(const void *it) { const struct cx_iterator_s *iter = it; - const struct cx_list_s *list = iter->src_handle.c; + const struct cx_list_s *list = iter->src_handle; return iter->index < list->collection.size; } @@ -941,23 +1104,25 @@ static void cx_arl_iter_next(void *it) { struct cx_iterator_s *iter = it; if (iter->base.remove) { iter->base.remove = false; - cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL); + cx_arl_remove(iter->src_handle, iter->index, 1, NULL); + iter->elem_count--; } else { iter->index++; iter->elem_handle = ((char *) iter->elem_handle) - + ((const struct cx_list_s *) iter->src_handle.c)->collection.elem_size; + + ((const struct cx_list_s *) iter->src_handle)->collection.elem_size; } } static void cx_arl_iter_prev(void *it) { struct cx_iterator_s *iter = it; - const cx_array_list *list = iter->src_handle.c; if (iter->base.remove) { iter->base.remove = false; - cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL); + cx_arl_remove(iter->src_handle, iter->index, 1, NULL); + iter->elem_count--; } iter->index--; + cx_array_list *list = iter->src_handle; if (iter->index < list->base.collection.size) { iter->elem_handle = ((char *) list->data) + iter->index * list->base.collection.elem_size; @@ -973,7 +1138,7 @@ static struct cx_iterator_s cx_arl_iterator( struct cx_iterator_s iter; iter.index = index; - iter.src_handle.c = list; + iter.src_handle = (void*)list; iter.elem_handle = cx_arl_at(list, index); iter.elem_size = list->collection.elem_size; iter.elem_count = list->collection.size; @@ -981,7 +1146,7 @@ static struct cx_iterator_s cx_arl_iterator( iter.base.current = cx_arl_iter_current; iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next; iter.base.remove = false; - iter.base.mutating = false; + iter.base.allow_remove = true; return iter; } @@ -991,6 +1156,7 @@ static cx_list_class cx_array_list_class = { cx_arl_insert_element, cx_arl_insert_array, cx_arl_insert_sorted, + cx_arl_insert_unique, cx_arl_insert_iter, cx_arl_remove, cx_arl_clear, diff --git a/ucx/compare.c b/ucx/compare.c index b260aff..1fc036d 100644 --- a/ucx/compare.c +++ b/ucx/compare.c @@ -198,6 +198,20 @@ int cx_cmp_uint64(const void *i1, const void *i2) { return cx_vcmp_uint64(a, b); } +int cx_vcmp_size(size_t a, size_t b) { + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_size(const void *i1, const void *i2) { + size_t a = *((const size_t *) i1); + size_t b = *((const size_t *) i2); + return cx_vcmp_size(a, b); +} + int cx_vcmp_float(float a, float b) { if (fabsf(a - b) < 1e-6f) { return 0; diff --git a/ucx/cx/allocator.h b/ucx/cx/allocator.h index c169a92..a69ef07 100644 --- a/ucx/cx/allocator.h +++ b/ucx/cx/allocator.h @@ -46,36 +46,22 @@ typedef struct { /** * The allocator's malloc() implementation. */ - void *(*malloc)( - void *data, - size_t n - ); + void *(*malloc)(void *data, size_t n); /** * The allocator's realloc() implementation. */ - void *(*realloc)( - void *data, - void *mem, - size_t n - ); + void *(*realloc)(void *data, void *mem, size_t n); /** * The allocator's calloc() implementation. */ - void *(*calloc)( - void *data, - size_t nmemb, - size_t size - ); + void *(*calloc)(void *data, size_t nmemb, size_t size); /** * The allocator's free() implementation. */ - void (*free)( - void *data, - void *mem - ); + void (*free)(void *data, void *mem); } cx_allocator_class; /** @@ -100,15 +86,13 @@ typedef struct cx_allocator_s CxAllocator; /** * A pre-defined allocator using standard library malloc() etc. */ -cx_attr_export -extern const CxAllocator * const cxStdlibAllocator; +CX_EXPORT extern const CxAllocator * const cxStdlibAllocator; /** * The default allocator that is used by UCX. * Initialized with cxStdlibAllocator, but you may change it. */ -cx_attr_export -extern const CxAllocator * cxDefaultAllocator; +CX_EXPORT extern const CxAllocator * cxDefaultAllocator; /** * Function pointer type for destructor functions. @@ -133,10 +117,33 @@ typedef void (*cx_destructor_func)(void *memory); * @param data an optional pointer to custom data * @param memory a pointer to the object to destruct */ -typedef void (*cx_destructor_func2)( - void *data, - void *memory -); +typedef void (*cx_destructor_func2)(void *data, void *memory); + + +/** + * Function pointer type for clone functions. + * + * A clone function is supposed to create a deep copy of the memory pointed to + * by the @p source pointer. + * If the @p target pointer is non-null, the clone function is supposed to store + * the copy into that memory region. + * Otherwise, the clone function shall use the specified @p allocator to create + * a new object. + * + * The return value of a clone function is always a pointer to the target + * memory region, or @c NULL if any allocation failed. + * A clone function SHOULD NOT fail for any other reason than an allocation + * failure. + * + * @param target the target memory or @c NULL, if memory shall be allocated + * @param source the source memory + * @param allocator the allocator that shall be used + * @param data optional additional data + * @return either the specified @p target, a pointer to the allocated memory, + * or @c NULL, if any error occurred + */ +typedef void*(cx_clone_func)(void *target, const void *source, + const CxAllocator *allocator, void *data); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -153,13 +160,8 @@ typedef void (*cx_destructor_func2)( * @retval non-zero failure * @see cx_reallocatearray() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_reallocate_( - void **mem, - size_t n -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_reallocate_(void **mem, size_t n); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -180,14 +182,8 @@ int cx_reallocate_( * @retval non-zero failure * @see cx_reallocate() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_reallocatearray_( - void **mem, - size_t nmemb, - size_t size -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_reallocatearray_(void **mem, size_t nmemb, size_t size); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -244,11 +240,7 @@ int cx_reallocatearray_( * @param mem a pointer to the block to free */ cx_attr_nonnull_arg(1) -cx_attr_export -void cxFree( - const CxAllocator *allocator, - void *mem -); +CX_EXPORT void cxFree(const CxAllocator *allocator, void *mem); /** * Allocate @p n bytes of memory. @@ -257,21 +249,14 @@ void cxFree( * @param n the number of bytes * @return a pointer to the allocated memory */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_malloc -cx_attr_dealloc_ucx -cx_attr_allocsize(2) -cx_attr_export -void *cxMalloc( - const CxAllocator *allocator, - size_t n -); +cx_attr_nodiscard cx_attr_nonnull +cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2) +CX_EXPORT void *cxMalloc(const CxAllocator *allocator, size_t n); /** * Reallocate the previously allocated block in @p mem, making the new block * @p n bytes long. - * This function may return the same pointer that was passed to it, if moving + * This function may return the same pointer passed to it if moving * the memory was not necessary. * * @note Re-allocating a block allocated by a different allocator is undefined. @@ -281,25 +266,18 @@ void *cxMalloc( * @param n the new size in bytes * @return a pointer to the reallocated memory */ -cx_attr_nodiscard -cx_attr_nonnull_arg(1) -cx_attr_dealloc_ucx -cx_attr_allocsize(3) -cx_attr_export -void *cxRealloc( - const CxAllocator *allocator, - void *mem, - size_t n -); +cx_attr_nodiscard cx_attr_nonnull_arg(1) +cx_attr_dealloc_ucx cx_attr_allocsize(3) +CX_EXPORT void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n); /** * Reallocate the previously allocated block in @p mem, making the new block * @p n bytes long. - * This function may return the same pointer that was passed to it, if moving + * This function may return the same pointer passed to it if moving * the memory was not necessary. * * The size is calculated by multiplying @p nemb and @p size. - * If that multiplication overflows, this function returns @c NULL and @c errno + * If that multiplication overflows, this function returns @c NULL, and @c errno * will be set. * * @note Re-allocating a block allocated by a different allocator is undefined. @@ -310,17 +288,10 @@ void *cxRealloc( * @param size the size of each element * @return a pointer to the reallocated memory */ -cx_attr_nodiscard -cx_attr_nonnull_arg(1) -cx_attr_dealloc_ucx -cx_attr_allocsize(3, 4) -cx_attr_export -void *cxReallocArray( - const CxAllocator *allocator, - void *mem, - size_t nmemb, - size_t size -); +cx_attr_nodiscard cx_attr_nonnull_arg(1) +cx_attr_dealloc_ucx cx_attr_allocsize(3, 4) +CX_EXPORT void *cxReallocArray(const CxAllocator *allocator, + void *mem, size_t nmemb, size_t size); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -330,7 +301,7 @@ void *cxReallocArray( * @note Re-allocating a block allocated by a different allocator is undefined. * * @par Error handling - * @c errno will be set, if the underlying realloc function does so. + * @c errno will be set if the underlying realloc function does so. * * @param allocator the allocator * @param mem pointer to the pointer to allocated block @@ -338,14 +309,8 @@ void *cxReallocArray( * @retval zero success * @retval non-zero failure */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -int cxReallocate_( - const CxAllocator *allocator, - void **mem, - size_t n -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -355,7 +320,7 @@ int cxReallocate_( * @note Re-allocating a block allocated by a different allocator is undefined. * * @par Error handling - * @c errno will be set, if the underlying realloc function does so. + * @c errno will be set if the underlying realloc function does so. * * @param allocator (@c CxAllocator*) the allocator * @param mem (@c void**) pointer to the pointer to allocated block @@ -385,15 +350,9 @@ int cxReallocate_( * @retval zero success * @retval non-zero on failure */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -int cxReallocateArray_( - const CxAllocator *allocator, - void **mem, - size_t nmemb, - size_t size -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cxReallocateArray_(const CxAllocator *allocator, + void **mem, size_t nmemb, size_t size); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -425,17 +384,9 @@ int cxReallocateArray_( * @param size the size of each element in bytes * @return a pointer to the allocated memory */ -cx_attr_nonnull_arg(1) -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc_ucx -cx_attr_allocsize(2, 3) -cx_attr_export -void *cxCalloc( - const CxAllocator *allocator, - size_t nmemb, - size_t size -); +cx_attr_nonnull_arg(1) cx_attr_nodiscard +cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2, 3) +CX_EXPORT void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size); /** * Allocate @p n bytes of memory and sets every byte to zero. @@ -444,16 +395,9 @@ void *cxCalloc( * @param n the number of bytes * @return a pointer to the allocated memory */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_malloc -cx_attr_dealloc_ucx -cx_attr_allocsize(2) -cx_attr_export -void *cxZalloc( - const CxAllocator *allocator, - size_t n -); +cx_attr_nodiscard cx_attr_nonnull +cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2) +CX_EXPORT void *cxZalloc(const CxAllocator *allocator, size_t n); /** * Convenience macro that invokes cxMalloc() with the cxDefaultAllocator. diff --git a/ucx/cx/array_list.h b/ucx/cx/array_list.h index b38710d..97855dd 100644 --- a/ucx/cx/array_list.h +++ b/ucx/cx/array_list.h @@ -44,11 +44,10 @@ extern "C" { #endif /** - * The maximum item size in an array list that fits into stack buffer - * when swapped. + * The maximum item size in an array list that fits into + * a stack buffer when swapped. */ -cx_attr_export -extern const unsigned cx_array_swap_sbo_size; +CX_EXPORT extern const unsigned cx_array_swap_sbo_size; /** * Declares variables for an array that can be used with the convenience macros. @@ -84,7 +83,7 @@ extern const unsigned cx_array_swap_sbo_size; /** * Declares variables for an array that can be used with the convenience macros. * - * The size and capacity variables will have @c size_t type. + * The size and capacity variables will have type @c size_t. * Use #CX_ARRAY_DECLARE_SIZED() to specify a different type. * * @par Examples @@ -147,7 +146,7 @@ extern const unsigned cx_array_swap_sbo_size; * const CxAllocator *al = // ... * cx_array_initialize_a(al, myarray, 128); * // ... - * cxFree(al, myarray); // don't forget to free with same allocator + * cxFree(al, myarray); // remember to free with the same allocator * @endcode * * @param allocator (@c CxAllocator*) the allocator @@ -172,10 +171,9 @@ struct cx_array_reallocator_s { * Reallocates space for the given array. * * Implementations are not required to free the original array. - * This allows reallocation of static memory by allocating heap memory - * 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. + * This allows reallocation of static or stack memory by allocating heap memory + * and copying the array contents; namely when @c stack_ptr in this struct + * is not @c NULL and @p array equals @c stack_ptr. * * @param array the array to reallocate * @param old_capacity the old number of elements @@ -184,33 +182,18 @@ struct cx_array_reallocator_s { * @param alloc a reference to this allocator * @return a pointer to the reallocated memory or @c NULL on failure */ - cx_attr_nodiscard - cx_attr_nonnull_arg(5) - cx_attr_allocsize(3, 4) - void *(*realloc)( - void *array, - size_t old_capacity, - size_t new_capacity, - size_t elem_size, - struct cx_array_reallocator_s *alloc - ); + void *(*realloc)( void *array, size_t old_capacity, size_t new_capacity, + size_t elem_size, struct cx_array_reallocator_s *alloc); /** - * Custom data pointer. + * The allocator that shall be used for the reallocations. */ - void *ptr1; + const CxAllocator *allocator; /** - * Custom data pointer. + * Optional pointer to stack memory + * if the array is originally located on the stack. */ - void *ptr2; - /** - * Custom data integer. - */ - size_t int1; - /** - * Custom data integer. - */ - size_t int2; + const void *stack_ptr; }; /** @@ -221,32 +204,28 @@ typedef struct cx_array_reallocator_s CxArrayReallocator; /** * A default array reallocator that is based on the cxDefaultAllocator. */ -cx_attr_export -extern CxArrayReallocator *cx_array_default_reallocator; +CX_EXPORT extern CxArrayReallocator *cx_array_default_reallocator; /** * Creates a new array reallocator. * * When @p allocator is @c NULL, the cxDefaultAllocator will be used. * - * When @p stackmem is not @c NULL, the reallocator is supposed to be used - * @em only for the specific array that is initially located at @p stackmem. - * When reallocation is needed, the reallocator checks, if the array is - * still located at @p stackmem and copies the contents to the heap. + * When @p stack_ptr is not @c NULL, the reallocator is supposed to be used + * @em only for the specific array initially located at @p stack_ptr. + * When reallocation is needed, the reallocator checks if the array is + * still located at @p stack_ptr and copies the contents to the heap. * - * @note Invoking this function with both arguments @c NULL will return a + * @note Invoking this function with both arguments being @c NULL will return a * reallocator that behaves like #cx_array_default_reallocator. * * @param allocator the allocator this reallocator shall be based on - * @param stackmem the address of the array when the array is initially located - * on the stack or shall not reallocated in place + * @param stack_ptr the address of the array when the array is initially located + * on the stack or shall not reallocate in place * @return an array reallocator */ -cx_attr_export -CxArrayReallocator cx_array_reallocator( - const struct cx_allocator_s *allocator, - const void *stackmem -); +CX_EXPORT CxArrayReallocator cx_array_reallocator( + const struct cx_allocator_s *allocator, const void *stack_ptr); /** * Reserves memory for additional elements. @@ -263,7 +242,7 @@ CxArrayReallocator cx_array_reallocator( * * The @p width in bytes refers to the size and capacity. * Both must have the same width. - * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64 bit + * 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. * * @param array a pointer to the target array @@ -279,16 +258,9 @@ CxArrayReallocator cx_array_reallocator( * @see cx_array_reallocator() */ cx_attr_nonnull_arg(1, 2, 3) -cx_attr_export -int cx_array_reserve( - void **array, - void *size, - void *capacity, - unsigned width, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator -); +CX_EXPORT int cx_array_reserve(void **array, void *size, void *capacity, + unsigned width, size_t elem_size, size_t elem_count, + CxArrayReallocator *reallocator); /** * Copies elements from one array to another. @@ -296,7 +268,7 @@ int cx_array_reserve( * The elements are copied to the @p target array at the specified @p index, * overwriting possible elements. The @p index does not need to be in range of * the current array @p size. If the new index plus the number of elements added - * would extend the array's size, the remaining @p capacity is used. + * extends the array's size, the remaining @p capacity is used. * * If the @p capacity is also insufficient to hold the new data, a reallocation * attempt is made with the specified @p reallocator. @@ -305,7 +277,7 @@ int cx_array_reserve( * * The @p width in bytes refers to the size and capacity. * Both must have the same width. - * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64 bit + * 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. * * @param target a pointer to the target array @@ -323,18 +295,9 @@ int cx_array_reserve( * @see cx_array_reallocator() */ cx_attr_nonnull_arg(1, 2, 3, 6) -cx_attr_export -int cx_array_copy( - void **target, - void *size, - void *capacity, - unsigned width, - size_t index, - const void *src, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator -); +CX_EXPORT int cx_array_copy(void **target, void *size, void *capacity, unsigned width, + size_t index, const void *src, size_t elem_size, size_t elem_count, + CxArrayReallocator *reallocator); /** * Convenience macro that uses cx_array_copy() with a default layout and @@ -482,17 +445,9 @@ int cx_array_copy( * @retval non-zero failure */ cx_attr_nonnull_arg(1, 2, 3, 5) -cx_attr_export -int cx_array_insert_sorted( - void **target, - size_t *size, - size_t *capacity, - cx_compare_func cmp_func, - const void *src, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator -); +CX_EXPORT int cx_array_insert_sorted(void **target, size_t *size, size_t *capacity, + cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count, + CxArrayReallocator *reallocator); /** * Inserts an element into a sorted array. @@ -500,7 +455,7 @@ int cx_array_insert_sorted( * If the target array is not already sorted with respect * to the specified @p cmp_func, the behavior is undefined. * - * If the capacity is insufficient to hold the new data, a reallocation + * If the capacity is not enough to hold the new data, a reallocation * attempt is made. * * The \@ SIZE_TYPE is flexible and can be any unsigned integer type. @@ -586,6 +541,127 @@ int cx_array_insert_sorted( #define cx_array_simple_insert_sorted(array, src, n, cmp_func) \ cx_array_simple_insert_sorted_a(NULL, array, src, n, cmp_func) + +/** + * Inserts a sorted array into another sorted array, avoiding duplicates. + * + * If either the target or the source array is not already sorted with respect + * to the specified @p cmp_func, the behavior is undefined. + * + * If the capacity is insufficient to hold the new data, a reallocation + * attempt is made. + * You can create your own reallocator by hand, use #cx_array_default_reallocator, + * or use the convenience function cx_array_reallocator() to create a custom reallocator. + * + * @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 + * @param cmp_func the compare function for the elements + * @param src the source array + * @param elem_size the size of one element + * @param elem_count the number of elements to insert + * @param reallocator the array reallocator to use + * (@c NULL defaults to #cx_array_default_reallocator) + * @retval zero success + * @retval non-zero failure + */ +cx_attr_nonnull_arg(1, 2, 3, 5) +CX_EXPORT int cx_array_insert_unique(void **target, size_t *size, size_t *capacity, + cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count, + CxArrayReallocator *reallocator); + +/** + * Inserts an element into a sorted array if it does not exist. + * + * If the target array is not already sorted with respect + * to the specified @p cmp_func, the behavior is undefined. + * + * If the capacity is insufficient to hold the new data, a reallocation + * attempt is made. + * + * The \@ SIZE_TYPE is flexible and can be any unsigned integer type. + * It is important, however, that @p size and @p capacity are pointers to + * variables of the same type. + * + * @param target (@c void**) a pointer to the target array + * @param size (@c SIZE_TYPE*) a pointer to the size of the target array + * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array + * @param elem_size (@c size_t) the size of one element + * @param elem (@c void*) a pointer to the element to add + * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @param reallocator (@c CxArrayReallocator*) the array reallocator to use + * @retval zero success (also when the element was already present) + * @retval non-zero failure + */ +#define cx_array_add_unique(target, size, capacity, elem_size, elem, cmp_func, reallocator) \ + cx_array_insert_unique((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator) + +/** + * Convenience macro for cx_array_add_unique() with a default + * layout and the specified reallocator. + * + * @param reallocator (@c CxArrayReallocator*) the array reallocator to use + * @param array the name of the array (NOT a pointer or alias to the array) + * @param elem the element to add (NOT a pointer, address is automatically taken) + * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @retval zero success + * @retval non-zero failure + * @see CX_ARRAY_DECLARE() + * @see cx_array_simple_add_unique() + */ +#define cx_array_simple_add_unique_a(reallocator, array, elem, cmp_func) \ + cx_array_add_unique(&array, &(array##_size), &(array##_capacity), \ + sizeof((array)[0]), &(elem), cmp_func, reallocator) + +/** + * Convenience macro for cx_array_add_unique() with a default + * layout and the default reallocator. + * + * @param array the name of the array (NOT a pointer or alias to the array) + * @param elem the element to add (NOT a pointer, address is automatically taken) + * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @retval zero success + * @retval non-zero failure + * @see CX_ARRAY_DECLARE() + * @see cx_array_simple_add_unique_a() + */ +#define cx_array_simple_add_unique(array, elem, cmp_func) \ + cx_array_simple_add_unique_a(NULL, array, elem, cmp_func) + +/** + * Convenience macro for cx_array_insert_unique() with a default + * layout and the specified reallocator. + * + * @param reallocator (@c CxArrayReallocator*) the array reallocator to use + * @param array the name of the array (NOT a pointer or alias to the array) + * @param src (@c void*) pointer to the source array + * @param n (@c size_t) number of elements in the source array + * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @retval zero success + * @retval non-zero failure + * @see CX_ARRAY_DECLARE() + * @see cx_array_simple_insert_unique() + */ +#define cx_array_simple_insert_unique_a(reallocator, array, src, n, cmp_func) \ + cx_array_insert_unique((void**)(&array), &(array##_size), &(array##_capacity), \ + cmp_func, src, sizeof((array)[0]), n, reallocator) + +/** + * Convenience macro for cx_array_insert_unique() with a default + * layout and the default reallocator. + * + * @param array the name of the array (NOT a pointer or alias to the array) + * @param src (@c void*) pointer to the source array + * @param n (@c size_t) number of elements in the source array + * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @retval zero success + * @retval non-zero failure + * @see CX_ARRAY_DECLARE() + * @see cx_array_simple_insert_unique_a() + */ +#define cx_array_simple_insert_unique(array, src, n, cmp_func) \ + cx_array_simple_insert_unique_a(NULL, array, src, n, cmp_func) + /** * Searches the largest lower bound in a sorted array. * @@ -609,14 +685,8 @@ int cx_array_insert_sorted( * @see cx_array_binary_search() */ cx_attr_nonnull -cx_attr_export -size_t cx_array_binary_search_inf( - const void *arr, - size_t size, - size_t elem_size, - const void *elem, - cx_compare_func cmp_func -); +CX_EXPORT size_t cx_array_binary_search_inf(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func cmp_func); /** * Searches an item in a sorted array. @@ -635,14 +705,8 @@ size_t cx_array_binary_search_inf( * @see cx_array_binary_search_sup() */ cx_attr_nonnull -cx_attr_export -size_t cx_array_binary_search( - const void *arr, - size_t size, - size_t elem_size, - const void *elem, - cx_compare_func cmp_func -); +CX_EXPORT size_t cx_array_binary_search(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func cmp_func); /** * Searches the smallest upper bound in a sorted array. @@ -667,37 +731,25 @@ size_t cx_array_binary_search( * @see cx_array_binary_search() */ cx_attr_nonnull -cx_attr_export -size_t cx_array_binary_search_sup( - const void *arr, - size_t size, - size_t elem_size, - const void *elem, - cx_compare_func cmp_func -); +CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func cmp_func); /** * Swaps two array elements. * * @param arr the array * @param elem_size the element size - * @param idx1 index of first element - * @param idx2 index of second element + * @param idx1 index of the first element + * @param idx2 index of the second element */ cx_attr_nonnull -cx_attr_export -void cx_array_swap( - void *arr, - size_t elem_size, - size_t idx1, - size_t idx2 -); +CX_EXPORT void cx_array_swap(void *arr, size_t elem_size, size_t idx1, size_t idx2); /** * Allocates an array list for storing elements with @p elem_size bytes each. * * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of - * copies of the added elements and the compare function will be automatically set + * copies of the added elements, 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 @@ -712,13 +764,8 @@ void cx_array_swap( cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) -cx_attr_export -CxList *cxArrayListCreate( - const CxAllocator *allocator, - cx_compare_func comparator, - size_t elem_size, - size_t initial_capacity -); +CX_EXPORT CxList *cxArrayListCreate(const CxAllocator *allocator, + cx_compare_func comparator, size_t elem_size, size_t initial_capacity); /** * Allocates an array list for storing elements with @p elem_size bytes each. diff --git a/ucx/cx/buffer.h b/ucx/cx/buffer.h index 867fb30..6324a0b 100644 --- a/ucx/cx/buffer.h +++ b/ucx/cx/buffer.h @@ -62,7 +62,7 @@ extern "C" { * If this flag is enabled, the buffer will automatically free its contents when destroyed. * * Do NOT set this flag together with #CX_BUFFER_COPY_ON_WRITE. It will be automatically - * set when the copy-on-write operations is performed. + * set when the copy-on-write operation is performed. */ #define CX_BUFFER_FREE_CONTENTS 0x01 @@ -74,7 +74,7 @@ extern "C" { /** * If this flag is enabled, the buffer will allocate new memory when written to. * - * The current contents of the buffer will be copied to the new memory and the flag + * The current contents of the buffer will be copied to the new memory, and the flag * will be cleared while the #CX_BUFFER_FREE_CONTENTS flag will be set automatically. */ #define CX_BUFFER_COPY_ON_WRITE 0x04 @@ -127,7 +127,7 @@ struct cx_buffer_flush_config_s { size_t blkmax; /** - * The target for write function. + * The target for the write function. */ void *target; @@ -202,7 +202,7 @@ typedef struct cx_buffer_s CxBuffer; * you will need to cast the pointer, and you should set the * #CX_BUFFER_COPY_ON_WRITE flag. * - * You need to set the size manually after initialization, if + * You need to set the size manually after initialization if * you provide @p space which already contains data. * * When you specify stack memory as @p space and decide to use @@ -210,7 +210,7 @@ typedef struct cx_buffer_s CxBuffer; * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the * #CX_BUFFER_AUTO_EXTEND flag. * - * @note You may provide @c NULL as argument for @p space. + * @note You may provide @c NULL as the argument for @p space. * Then this function will allocate the space and enforce * the #CX_BUFFER_FREE_CONTENTS flag. In that case, specifying * copy-on-write should be avoided, because the allocated @@ -227,14 +227,8 @@ typedef struct cx_buffer_s CxBuffer; * @return zero on success, non-zero if a required allocation failed */ cx_attr_nonnull_arg(1) -cx_attr_export -int cxBufferInit( - CxBuffer *buffer, - void *space, - size_t capacity, - const CxAllocator *allocator, - int flags -); +CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity, + const CxAllocator *allocator, int flags); /** * Configures the buffer for flushing. @@ -251,11 +245,7 @@ int cxBufferInit( * @see cxBufferWrite() */ cx_attr_nonnull -cx_attr_export -int cxBufferEnableFlushing( - CxBuffer *buffer, - CxBufferFlushConfig config -); +CX_EXPORT int cxBufferEnableFlushing(CxBuffer *buffer, CxBufferFlushConfig config); /** * Destroys the buffer contents. @@ -267,8 +257,7 @@ int cxBufferEnableFlushing( * @see cxBufferInit() */ cx_attr_nonnull -cx_attr_export -void cxBufferDestroy(CxBuffer *buffer); +CX_EXPORT void cxBufferDestroy(CxBuffer *buffer); /** * Deallocates the buffer. @@ -276,14 +265,10 @@ void cxBufferDestroy(CxBuffer *buffer); * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys * the contents. If you @em only want to destroy the contents, use cxBufferDestroy(). * - * @remark As with all free() functions, this accepts @c NULL arguments in which - * case it does nothing. - * * @param buffer the buffer to deallocate * @see cxBufferCreate() */ -cx_attr_export -void cxBufferFree(CxBuffer *buffer); +CX_EXPORT void cxBufferFree(CxBuffer *buffer); /** * Allocates and initializes a fresh buffer. @@ -296,7 +281,7 @@ void cxBufferFree(CxBuffer *buffer); * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the * #CX_BUFFER_AUTO_EXTEND flag. * - * @note You may provide @c NULL as argument for @p space. + * @note You may provide @c NULL as the argument for @p space. * Then this function will allocate the space and enforce * the #CX_BUFFER_FREE_CONTENTS flag. * @@ -309,16 +294,9 @@ void cxBufferFree(CxBuffer *buffer); * @param flags buffer features (see cx_buffer_s.flags) * @return a pointer to the buffer on success, @c NULL if a required allocation failed */ -cx_attr_malloc -cx_attr_dealloc(cxBufferFree, 1) -cx_attr_nodiscard -cx_attr_export -CxBuffer *cxBufferCreate( - void *space, - size_t capacity, - const CxAllocator *allocator, - int flags -); +cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard +CX_EXPORT CxBuffer *cxBufferCreate(void *space, size_t capacity, + const CxAllocator *allocator, int flags); /** * Shifts the contents of the buffer by the given offset. @@ -327,8 +305,8 @@ CxBuffer *cxBufferCreate( * If auto extension is enabled, the buffer grows, if necessary. * In case the auto extension fails, this function returns a non-zero value and * no contents are changed. - * If auto extension is disabled, the contents that do not fit into the buffer - * are discarded. + * When the auto extension is disabled, the contents that do not fit into the + * buffer are discarded. * * If the offset is negative, the contents are shifted to the left where the * first @p shift bytes are discarded. @@ -336,15 +314,15 @@ CxBuffer *cxBufferCreate( * If this value is larger than the buffer size, the buffer is emptied (but * not cleared, see the security note below). * - * The buffer position gets shifted alongside with the content but is kept + * The buffer position gets shifted alongside the content but is kept * within the boundaries of the buffer. * * @note For situations where @c off_t is not large enough, there are specialized cxBufferShiftLeft() and - * cxBufferShiftRight() functions using a @c size_t as parameter type. + * cxBufferShiftRight() functions using a @c size_t as the parameter type. * * @attention * Security Note: The shifting operation does @em not erase the previously occupied memory cells. - * But you can easily do that manually, e.g. by calling + * But you can do that manually by calling * memset(buffer->bytes, 0, shift) for a right shift or * memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size) * for a left shift. @@ -357,11 +335,7 @@ CxBuffer *cxBufferCreate( * @see cxBufferShiftRight() */ cx_attr_nonnull -cx_attr_export -int cxBufferShift( - CxBuffer *buffer, - off_t shift -); +CX_EXPORT int cxBufferShift(CxBuffer *buffer, off_t shift); /** * Shifts the buffer to the right. @@ -374,11 +348,7 @@ int cxBufferShift( * @see cxBufferShift() */ cx_attr_nonnull -cx_attr_export -int cxBufferShiftRight( - CxBuffer *buffer, - size_t shift -); +CX_EXPORT int cxBufferShiftRight(CxBuffer *buffer, size_t shift); /** * Shifts the buffer to the left. @@ -391,11 +361,7 @@ int cxBufferShiftRight( * @see cxBufferShift() */ cx_attr_nonnull -cx_attr_export -int cxBufferShiftLeft( - CxBuffer *buffer, - size_t shift -); +CX_EXPORT int cxBufferShiftLeft(CxBuffer *buffer, size_t shift); /** @@ -419,12 +385,7 @@ int cxBufferShiftLeft( * */ cx_attr_nonnull -cx_attr_export -int cxBufferSeek( - CxBuffer *buffer, - off_t offset, - int whence -); +CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence); /** * Clears the buffer by resetting the position and deleting the data. @@ -439,8 +400,7 @@ int cxBufferSeek( * @see cxBufferReset() */ cx_attr_nonnull -cx_attr_export -void cxBufferClear(CxBuffer *buffer); +CX_EXPORT void cxBufferClear(CxBuffer *buffer); /** * Resets the buffer by resetting the position and size to zero. @@ -452,8 +412,7 @@ void cxBufferClear(CxBuffer *buffer); * @see cxBufferClear() */ cx_attr_nonnull -cx_attr_export -void cxBufferReset(CxBuffer *buffer); +CX_EXPORT void cxBufferReset(CxBuffer *buffer); /** * Tests, if the buffer position has exceeded the buffer size. @@ -463,10 +422,8 @@ void cxBufferReset(CxBuffer *buffer); * byte of the buffer's contents * @retval false otherwise */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -bool cxBufferEof(const CxBuffer *buffer); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT bool cxBufferEof(const CxBuffer *buffer); /** @@ -484,11 +441,7 @@ bool cxBufferEof(const CxBuffer *buffer); * @see cxBufferShrink() */ cx_attr_nonnull -cx_attr_export -int cxBufferMinimumCapacity( - CxBuffer *buffer, - size_t capacity -); +CX_EXPORT int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity); /** * Shrinks the capacity of the buffer to fit its current size. @@ -507,17 +460,13 @@ int cxBufferMinimumCapacity( * @see cxBufferMinimumCapacity() */ cx_attr_nonnull -cx_attr_export -void cxBufferShrink( - CxBuffer *buffer, - size_t reserve -); +CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve); /** * Writes data to a CxBuffer. * * If automatic flushing is not enabled, the data is simply written into the - * buffer at the current position and the position of the buffer is increased + * buffer at the current position, and the position of the buffer is increased * by the number of bytes written. * * If flushing is enabled and the buffer needs to flush, the data is flushed to @@ -526,7 +475,7 @@ void cxBufferShrink( * data in this buffer is shifted to the beginning of this buffer so that the * newly available space can be used to append as much data as possible. * - * This function only stops writing more elements, when the flush target and this + * This function only stops writing more elements when the flush target and this * buffer are both incapable of taking more data or all data has been written. * * If, after flushing, the number of items that shall be written still exceeds @@ -534,14 +483,14 @@ void cxBufferShrink( * to the flush target, if possible. * * The number returned by this function is the number of elements from - * @c ptr that could be written to either the flush target or the buffer - * (so it does not include the number of items that had been already in the buffer - * in were flushed during the process). + * @c ptr that could be written to either the flush target or the buffer. + * That means it does @em not include the number of items that were already in + * the buffer and were also flushed during the process. * * @attention * When @p size is larger than one and the contents of the buffer are not aligned * with @p size, flushing stops after all complete items have been flushed, leaving - * the mis-aligned part in the buffer. + * the misaligned part in the buffer. * Afterward, this function only writes as many items as possible to the buffer. * * @note The signature is compatible with the fwrite() family of functions. @@ -555,13 +504,8 @@ void cxBufferShrink( * @see cxBufferRead() */ cx_attr_nonnull -cx_attr_export -size_t cxBufferWrite( - const void *ptr, - size_t size, - size_t nitems, - CxBuffer *buffer -); +CX_EXPORT size_t cxBufferWrite(const void *ptr, size_t size, + size_t nitems, CxBuffer *buffer); /** * Appends data to a CxBuffer. @@ -583,13 +527,8 @@ size_t cxBufferWrite( * @see cxBufferRead() */ cx_attr_nonnull -cx_attr_export -size_t cxBufferAppend( - const void *ptr, - size_t size, - size_t nitems, - CxBuffer *buffer -); +CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size, + size_t nitems, CxBuffer *buffer); /** * Performs a single flush-run on the specified buffer. @@ -614,19 +553,19 @@ size_t cxBufferAppend( * at position 200. The flush configuration is * @c blkmax=4 and @c blksize=64 . * Assume that the entire flush operation is successful. - * All 200 bytes on the left hand-side from the current + * All 200 bytes on the left-hand-side from the current * position are written. - * That means, the size of the buffer is now 140 and the + * That means the size of the buffer is now 140 and the * position is zero. * * @par Example 2 * Same as Example 1, but now the @c blkmax is 1. - * The size of the buffer is now 276 and the position is 136. + * The size of the buffer is now 276, and the position is 136. * * @par Example 3 * Same as Example 1, but now assume the flush target * only accepts 100 bytes before returning zero. - * That means, the flush operations manages to flush + * That means the flush operation manages to flush * one complete block and one partial block, ending * up with a buffer with size 240 and position 100. * @@ -636,8 +575,8 @@ size_t cxBufferAppend( * @remark When the buffer uses copy-on-write, the memory * is copied first, before attempting any flush. * This is, however, considered an erroneous use of the - * buffer, because it does not make much sense to put - * readonly data into an UCX buffer for flushing, instead + * buffer because it makes little sense to put + * readonly data into an UCX buffer for flushing instead * of writing it directly to the target. * * @param buffer the buffer @@ -645,8 +584,7 @@ size_t cxBufferAppend( * @see cxBufferEnableFlushing() */ cx_attr_nonnull -cx_attr_export -size_t cxBufferFlush(CxBuffer *buffer); +CX_EXPORT size_t cxBufferFlush(CxBuffer *buffer); /** * Reads data from a CxBuffer. @@ -664,13 +602,8 @@ size_t cxBufferFlush(CxBuffer *buffer); * @see cxBufferAppend() */ cx_attr_nonnull -cx_attr_export -size_t cxBufferRead( - void *ptr, - size_t size, - size_t nitems, - CxBuffer *buffer -); +CX_EXPORT size_t cxBufferRead(void *ptr, size_t size, + size_t nitems, CxBuffer *buffer); /** * Writes a character to a buffer. @@ -678,9 +611,9 @@ size_t cxBufferRead( * The least significant byte of the argument is written to the buffer. If the * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled, * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature - * is disabled or buffer extension fails, @c EOF is returned. + * is disabled or the buffer extension fails, @c EOF is returned. * - * On successful write, the position of the buffer is increased. + * On successful writing, the position of the buffer is increased. * * If you just want to write a null-terminator at the current position, you * should use cxBufferTerminate() instead. @@ -688,15 +621,11 @@ size_t cxBufferRead( * @param buffer the buffer to write to * @param c the character to write * @return the byte that has been written or @c EOF when the end of the stream is - * reached and automatic extension is not enabled or not possible + * reached, and automatic extension is not enabled or not possible * @see cxBufferTerminate() */ cx_attr_nonnull -cx_attr_export -int cxBufferPut( - CxBuffer *buffer, - int c -); +CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c); /** * Writes a terminating zero to a buffer at the current position. @@ -710,8 +639,7 @@ int cxBufferPut( * @return zero, if the terminator could be written, non-zero otherwise */ cx_attr_nonnull -cx_attr_export -int cxBufferTerminate(CxBuffer *buffer); +CX_EXPORT int cxBufferTerminate(CxBuffer *buffer); /** * Writes a string to a buffer. @@ -722,13 +650,8 @@ int cxBufferTerminate(CxBuffer *buffer); * @param str the zero-terminated string * @return the number of bytes written */ -cx_attr_nonnull -cx_attr_cstr_arg(2) -cx_attr_export -size_t cxBufferPutString( - CxBuffer *buffer, - const char *str -); +cx_attr_nonnull cx_attr_cstr_arg(2) +CX_EXPORT size_t cxBufferPutString(CxBuffer *buffer, const char *str); /** * Gets a character from a buffer. @@ -739,8 +662,7 @@ size_t cxBufferPutString( * @return the character or @c EOF, if the end of the buffer is reached */ cx_attr_nonnull -cx_attr_export -int cxBufferGet(CxBuffer *buffer); +CX_EXPORT int cxBufferGet(CxBuffer *buffer); #ifdef __cplusplus } diff --git a/ucx/cx/collection.h b/ucx/cx/collection.h index ba329dc..8e900d3 100644 --- a/ucx/cx/collection.h +++ b/ucx/cx/collection.h @@ -142,14 +142,16 @@ struct cx_collection_s { /** * Indicates whether the collection can guarantee that the stored elements are currently sorted. * - * This may return false even when the elements are sorted. - * It is totally up to the implementation of the collection whether it keeps track of the order of its elements. + * This may return @c false even when the elements are sorted. + * It is totally up to the implementation of the collection when to check if the elements are sorted. + * It is usually a good practice to establish this property as an invariant that does not need + * to be re-checked on certain operations. * * @param c a pointer to a struct that contains #CX_COLLECTION_BASE * @retval true if the elements are currently sorted wrt. the collection's compare function * @retval false if the order of elements is unknown */ -#define cxCollectionSorted(c) ((c)->collection.sorted) +#define cxCollectionSorted(c) ((c)->collection.sorted || (c)->collection.size == 0) /** * Sets a simple destructor function for this collection. diff --git a/ucx/cx/common.h b/ucx/cx/common.h index 0fc9e47..45a61d5 100644 --- a/ucx/cx/common.h +++ b/ucx/cx/common.h @@ -49,7 +49,7 @@ * https://uap-core.de/hg/ucx *

* - *

LICENCE

+ *

LICENSE

* * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. * @@ -130,6 +130,11 @@ #define __attribute__(x) #endif +/** + * Inform the compiler that falling through a switch case is intentional. + */ +#define cx_attr_fallthrough __attribute__((__fallthrough__)) + /** * All pointer arguments must be non-NULL. */ @@ -184,7 +189,7 @@ */ #define cx_attr_cstr_arg(idx) /** - * No support for access attribute in clang. + * No support for the access attribute in clang. */ #define cx_attr_access(mode, ...) #else @@ -260,15 +265,32 @@ #define _Thread_local __declspec(thread) #endif // _MSC_VER +// --------------------------------------------------------------------------- +// Exported and inlined functions +// --------------------------------------------------------------------------- + #if defined(CX_WINDLL_EXPORT) -#define cx_attr_export __declspec(dllexport) +#define CX_EXPORT __declspec(dllexport) #elif defined(CX_WINDLL) -#define cx_attr_export __declspec(dllimport) +#define CX_EXPORT __declspec(dllimport) #else /** Only used for building Windows DLLs. */ -#define cx_attr_export +#define CX_EXPORT #endif // CX_WINDLL / CX_WINDLL_EXPORT +#ifdef __GNUC__ +/** + * Declares a function to be inlined. + */ +#define CX_INLINE __attribute__((always_inline)) static inline +#else +#define CX_INLINE static inline +#endif +/** + * Declares a compatibility function for C++ builds. + */ +#define CX_CPPDECL static inline + // --------------------------------------------------------------------------- // Useful function pointers // --------------------------------------------------------------------------- @@ -276,22 +298,12 @@ /** * Function pointer compatible with fwrite-like functions. */ -typedef size_t (*cx_write_func)( - const void *, - size_t, - size_t, - void * -); +typedef size_t (*cx_write_func)(const void*, size_t, size_t, void*); /** * Function pointer compatible with fread-like functions. */ -typedef size_t (*cx_read_func)( - void *, - size_t, - size_t, - void * -); +typedef size_t (*cx_read_func)(void*, size_t, size_t, void*); // --------------------------------------------------------------------------- // Utility macros @@ -343,9 +355,7 @@ typedef size_t (*cx_read_func)( #if __cplusplus extern "C" #endif -cx_attr_export int cx_szmul_impl(size_t a, size_t b, size_t *result); +CX_EXPORT int cx_szmul_impl(size_t a, size_t b, size_t *result); #endif // cx_szmul - - #endif // UCX_COMMON_H diff --git a/ucx/cx/compare.h b/ucx/cx/compare.h index cb4ddf1..1b66e62 100644 --- a/ucx/cx/compare.h +++ b/ucx/cx/compare.h @@ -47,20 +47,14 @@ extern "C" { * * All functions from compare.h with the cx_cmp prefix are * compatible with this signature and can be used as - * compare function for collections, or other implementations + * compare function for collections or other implementations * that need to be type-agnostic. * * For simple comparisons the cx_vcmp family of functions * can be used, but they are NOT compatible with this function * pointer. */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -typedef int (*cx_compare_func)( - const void *left, - const void *right -); +typedef int (*cx_compare_func)(const void *left, const void *right); /** * Compares two integers of type int. @@ -74,10 +68,8 @@ typedef int (*cx_compare_func)( * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_int(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_int(const void *i1, const void *i2); /** * Compares two integers of type int. @@ -89,8 +81,7 @@ int cx_cmp_int(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_int(int i1, int i2); +CX_EXPORT int cx_vcmp_int(int i1, int i2); /** * Compares two integers of type long int. @@ -104,10 +95,8 @@ int cx_vcmp_int(int i1, int i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_longint(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_longint(const void *i1, const void *i2); /** * Compares two integers of type long int. @@ -119,8 +108,7 @@ int cx_cmp_longint(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_longint(long int i1, long int i2); +CX_EXPORT int cx_vcmp_longint(long int i1, long int i2); /** * Compares two integers of type long long. @@ -134,10 +122,8 @@ int cx_vcmp_longint(long int i1, long int i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_longlong(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_longlong(const void *i1, const void *i2); /** * Compares two integers of type long long. @@ -149,8 +135,7 @@ int cx_cmp_longlong(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_longlong(long long int i1, long long int i2); +CX_EXPORT int cx_vcmp_longlong(long long int i1, long long int i2); /** * Compares two integers of type int16_t. @@ -164,10 +149,8 @@ int cx_vcmp_longlong(long long int i1, long long int i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_int16(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_int16(const void *i1, const void *i2); /** * Compares two integers of type int16_t. @@ -179,8 +162,7 @@ int cx_cmp_int16(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_int16(int16_t i1, int16_t i2); +CX_EXPORT int cx_vcmp_int16(int16_t i1, int16_t i2); /** * Compares two integers of type int32_t. @@ -194,10 +176,8 @@ int cx_vcmp_int16(int16_t i1, int16_t i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_int32(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_int32(const void *i1, const void *i2); /** * Compares two integers of type int32_t. @@ -209,8 +189,7 @@ int cx_cmp_int32(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_int32(int32_t i1, int32_t i2); +CX_EXPORT int cx_vcmp_int32(int32_t i1, int32_t i2); /** * Compares two integers of type int64_t. @@ -224,10 +203,8 @@ int cx_vcmp_int32(int32_t i1, int32_t i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_int64(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_int64(const void *i1, const void *i2); /** * Compares two integers of type int64_t. @@ -239,8 +216,7 @@ int cx_cmp_int64(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_int64(int64_t i1, int64_t i2); +CX_EXPORT int cx_vcmp_int64(int64_t i1, int64_t i2); /** * Compares two integers of type unsigned int. @@ -254,10 +230,8 @@ int cx_vcmp_int64(int64_t i1, int64_t i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_uint(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uint(const void *i1, const void *i2); /** * Compares two integers of type unsigned int. @@ -269,8 +243,7 @@ int cx_cmp_uint(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_uint(unsigned int i1, unsigned int i2); +CX_EXPORT int cx_vcmp_uint(unsigned int i1, unsigned int i2); /** * Compares two integers of type unsigned long int. @@ -284,10 +257,8 @@ int cx_vcmp_uint(unsigned int i1, unsigned int i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_ulongint(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_ulongint(const void *i1, const void *i2); /** * Compares two integers of type unsigned long int. @@ -299,8 +270,7 @@ int cx_cmp_ulongint(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2); +CX_EXPORT int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2); /** * Compares two integers of type unsigned long long. @@ -314,10 +284,8 @@ int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_ulonglong(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_ulonglong(const void *i1, const void *i2); /** * Compares two integers of type unsigned long long. @@ -329,8 +297,7 @@ int cx_cmp_ulonglong(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2); +CX_EXPORT int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2); /** * Compares two integers of type uint16_t. @@ -344,10 +311,8 @@ int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_uint16(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uint16(const void *i1, const void *i2); /** * Compares two integers of type uint16_t. @@ -359,8 +324,7 @@ int cx_cmp_uint16(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_uint16(uint16_t i1, uint16_t i2); +CX_EXPORT int cx_vcmp_uint16(uint16_t i1, uint16_t i2); /** * Compares two integers of type uint32_t. @@ -374,10 +338,8 @@ int cx_vcmp_uint16(uint16_t i1, uint16_t i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_uint32(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uint32(const void *i1, const void *i2); /** * Compares two integers of type uint32_t. @@ -389,8 +351,7 @@ int cx_cmp_uint32(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_uint32(uint32_t i1, uint32_t i2); +CX_EXPORT int cx_vcmp_uint32(uint32_t i1, uint32_t i2); /** * Compares two integers of type uint64_t. @@ -404,10 +365,8 @@ int cx_vcmp_uint32(uint32_t i1, uint32_t i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_uint64(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uint64(const void *i1, const void *i2); /** * Compares two integers of type uint64_t. @@ -419,8 +378,34 @@ int cx_cmp_uint64(const void *i1, const void *i2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_uint64(uint64_t i1, uint64_t i2); +CX_EXPORT int cx_vcmp_uint64(uint64_t i1, uint64_t i2); + +/** + * Compares two integers of type size_t. + * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * + * @param i1 pointer to size_t one + * @param i2 pointer to size_t two + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument + */ +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_size(const void *i1, const void *i2); + +/** + * Compares two integers of type size_t. + * + * @param i1 size_t one + * @param i2 size_t two + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument + */ +cx_attr_nodiscard +CX_EXPORT int cx_vcmp_size(size_t i1, size_t i2); /** * Compares two real numbers of type float with precision 1e-6f. @@ -434,10 +419,8 @@ int cx_vcmp_uint64(uint64_t i1, uint64_t i2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_float(const void *f1, const void *f2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_float(const void *f1, const void *f2); /** * Compares two real numbers of type float with precision 1e-6f. @@ -449,8 +432,7 @@ int cx_cmp_float(const void *f1, const void *f2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_float(float f1, float f2); +CX_EXPORT int cx_vcmp_float(float f1, float f2); /** * Compares two real numbers of type double with precision 1e-14. @@ -464,10 +446,8 @@ int cx_vcmp_float(float f1, float f2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_double(const void *d1, const void *d2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_double(const void *d1, const void *d2); /** * Compares two real numbers of type double with precision 1e-14. @@ -479,8 +459,7 @@ int cx_cmp_double(const void *d1, const void *d2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_double(double d1, double d2); +CX_EXPORT int cx_vcmp_double(double d1, double d2); /** * Compares the integer representation of two pointers. @@ -494,10 +473,8 @@ int cx_vcmp_double(double d1, double d2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_intptr(const void *ptr1, const void *ptr2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_intptr(const void *ptr1, const void *ptr2); /** * Compares the integer representation of two pointers. @@ -509,8 +486,7 @@ int cx_cmp_intptr(const void *ptr1, const void *ptr2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2); +CX_EXPORT int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2); /** * Compares the unsigned integer representation of two pointers. @@ -524,10 +500,8 @@ int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_uintptr(const void *ptr1, const void *ptr2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uintptr(const void *ptr1, const void *ptr2); /** * Compares the unsigned integer representation of two pointers. @@ -539,8 +513,7 @@ int cx_cmp_uintptr(const void *ptr1, const void *ptr2); * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2); +CX_EXPORT int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2); /** * Compares the pointers specified in the arguments without dereferencing. @@ -551,10 +524,8 @@ int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2); * @retval 0 if both arguments are equal * @retval 1 if the left argument is greater than the right argument */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_cmp_ptr(const void *ptr1, const void *ptr2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_ptr(const void *ptr1, const void *ptr2); #ifdef __cplusplus } // extern "C" diff --git a/ucx/cx/hash_key.h b/ucx/cx/hash_key.h index cdf8864..c501e9c 100644 --- a/ucx/cx/hash_key.h +++ b/ucx/cx/hash_key.h @@ -46,14 +46,17 @@ extern "C" { /** Internal structure for a key within a hash map. */ struct cx_hash_key_s { - /** The key data. */ + /** + * The key data. + * May be NULL when the hash is collision-free. + */ const void *data; /** * The key data length. */ size_t len; /** The hash value of the key data. */ - unsigned hash; + uint64_t hash; }; /** @@ -76,8 +79,45 @@ typedef struct cx_hash_key_s CxHashKey; * @see cx_hash_key() */ cx_attr_nonnull -cx_attr_export -void cx_hash_murmur(CxHashKey *key); +CX_EXPORT void cx_hash_murmur(CxHashKey *key); + +/** + * Mixes up a 32-bit integer to be used as a hash. + * + * This function produces no collisions and has a good statistical distribution. + * + * @param x the integer + * @return the hash + */ +CX_EXPORT uint32_t cx_hash_u32(uint32_t x); + +/** + * Mixes up a 64-bit integer to be used as a hash. + * + * This function produces no collisions and has a good statistical distribution. + * + * @param x the integer + * @return the hash + */ +CX_EXPORT uint64_t cx_hash_u64(uint64_t x); + +/** + * Computes a hash key from a 32-bit integer. + * + * @param x the integer + * @return the hash key + */ +cx_attr_nodiscard +CX_EXPORT CxHashKey cx_hash_key_u32(uint32_t x); + +/** + * Computes a hash key from a 64-bit integer. + * + * @param x the integer + * @return the hash key + */ +cx_attr_nodiscard +CX_EXPORT CxHashKey cx_hash_key_u64(uint64_t x); /** * Computes a hash key from a string. @@ -87,10 +127,22 @@ void cx_hash_murmur(CxHashKey *key); * @param str the string * @return the hash key */ -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_export -CxHashKey cx_hash_key_str(const char *str); +cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT CxHashKey cx_hash_key_str(const char *str); + +/** + * Computes a hash key from a string. + * + * Use this function when the string is represented + * as an unsigned char array. + * + * The string needs to be zero-terminated. + * + * @param str the string + * @return the hash key + */ +cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT CxHashKey cx_hash_key_ustr(const unsigned char *str); /** * Computes a hash key from a byte array. @@ -99,13 +151,8 @@ CxHashKey cx_hash_key_str(const char *str); * @param len the length * @return the hash key */ -cx_attr_nodiscard -cx_attr_access_r(1, 2) -cx_attr_export -CxHashKey cx_hash_key_bytes( - const unsigned char *bytes, - size_t len -); +cx_attr_nodiscard cx_attr_access_r(1, 2) +CX_EXPORT CxHashKey cx_hash_key_bytes(const unsigned char *bytes, size_t len); /** * Computes a hash key for an arbitrary object. @@ -115,16 +162,12 @@ CxHashKey cx_hash_key_bytes( * used for data exchange with different machines. * * @param obj a pointer to an arbitrary object - * @param len the length of object in memory + * @param len the length of the object in memory * @return the hash key */ cx_attr_nodiscard cx_attr_access_r(1, 2) -cx_attr_export -CxHashKey cx_hash_key( - const void *obj, - size_t len -); +CX_EXPORT CxHashKey cx_hash_key(const void *obj, size_t len); /** * Computes a hash key from a UCX string. @@ -133,20 +176,98 @@ CxHashKey cx_hash_key( * @return the hash key */ cx_attr_nodiscard -static inline CxHashKey cx_hash_key_cxstr(cxstring str) { - return cx_hash_key(str.ptr, str.length); -} +CX_EXPORT CxHashKey cx_hash_key_cxstr(cxstring str); /** * Computes a hash key from a UCX string. * - * @param str (@c cxstring or @c cxmutstr) the string - * @return (@c CxHashKey) the hash key + * @param str the string + * @return the hash key + */ +cx_attr_nodiscard +CX_EXPORT CxHashKey cx_hash_key_mutstr(cxmutstr str); + +/** + * The identity function for the CX_HASH_KEY() macro. + * You should never need to use this manually. + * + * @param key the key + * @return a copy of the key + */ +cx_attr_nodiscard +CX_INLINE CxHashKey cx_hash_key_identity(CxHashKey key) { + return key; +} + +#ifndef __cplusplus +/** + * Creates a hash key from any of the supported types with implicit length. + * + * Does nothing when passing a CxHashkey. + * + * Supported types are UCX strings, zero-terminated C strings, + * and 32-bit or 64-bit unsigned integers. + * + * @param key the key data + * @returns the @c CxHashKey + */ +#define CX_HASH_KEY(key) _Generic((key), \ + CxHashKey: cx_hash_key_identity, \ + cxstring: cx_hash_key_cxstr, \ + cxmutstr: cx_hash_key_mutstr, \ + char*: cx_hash_key_str, \ + const char*: cx_hash_key_str, \ + unsigned char*: cx_hash_key_ustr, \ + const unsigned char*: cx_hash_key_ustr, \ + uint32_t: cx_hash_key_u32, \ + uint64_t: cx_hash_key_u64) \ + (key) +#endif // __cplusplus + +/** + * Compare function for hash keys. + * + * @param left the first key + * @param right the second key + * @return zero when the keys equal, non-zero when they differ */ -#define cx_hash_key_cxstr(str) cx_hash_key_cxstr(cx_strcast(str)) +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right); #ifdef __cplusplus } // extern "C" + +// ---------------------------------------------------------- +// Overloads of CX_HASH_KEY (the C++ version of a _Generic) +// ---------------------------------------------------------- + +CX_CPPDECL CxHashKey CX_HASH_KEY(CxHashKey key) { + return key; +} + +CX_CPPDECL CxHashKey CX_HASH_KEY(cxstring str) { + return cx_hash_key_cxstr(str); +} + +CX_CPPDECL CxHashKey CX_HASH_KEY(cxmutstr str) { + return cx_hash_key_mutstr(str); +} + +CX_CPPDECL CxHashKey CX_HASH_KEY(const char *str) { + return cx_hash_key_str(str); +} + +CX_CPPDECL CxHashKey CX_HASH_KEY(const unsigned char *str) { + return cx_hash_key_ustr(str); +} + +CX_CPPDECL CxHashKey CX_HASH_KEY(uint32_t key) { + return cx_hash_key_u32(key); +} + +CX_CPPDECL CxHashKey CX_HASH_KEY(uint64_t key) { + return cx_hash_key_u64(key); +} #endif #endif // UCX_HASH_KEY_H diff --git a/ucx/cx/hash_map.h b/ucx/cx/hash_map.h index 6b11e2c..3c9a303 100644 --- a/ucx/cx/hash_map.h +++ b/ucx/cx/hash_map.h @@ -73,7 +73,8 @@ struct cx_hash_map_s { * copies of the added elements. * * @note Iterators provided by this hash map implementation provide the remove operation. - * The index value of an iterator is incremented when the iterator advanced without removal. + * The index value of an iterator is incremented when the iterator advanced without + * removing an entry. * In other words, when the iterator is finished, @c index==size . * * @param allocator the allocator to use @@ -82,15 +83,9 @@ struct cx_hash_map_s { * @param buckets the initial number of buckets in this hash map * @return a pointer to the new hash map */ -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxMapFree, 1) -cx_attr_export -CxMap *cxHashMapCreate( - const CxAllocator *allocator, - size_t itemsize, - size_t buckets -); +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1) +CX_EXPORT CxMap *cxHashMapCreate(const CxAllocator *allocator, + size_t itemsize, size_t buckets); /** * Creates a new hash map with a default number of buckets. @@ -99,7 +94,8 @@ CxMap *cxHashMapCreate( * copies of the added elements. * * @note Iterators provided by this hash map implementation provide the remove operation. - * The index value of an iterator is incremented when the iterator advanced without removal. + * The index value of an iterator is incremented when the iterator advanced without + * removing an entry. * In other words, when the iterator is finished, @c index==size . * * @param itemsize (@c size_t) the size of one element @@ -111,10 +107,10 @@ CxMap *cxHashMapCreate( * Increases the number of buckets, if necessary. * * The load threshold is @c 0.75*buckets. If the element count exceeds the load - * threshold, the map will be rehashed. Otherwise, no action is performed and + * threshold, the map will be rehashed. Otherwise, no action is performed, and * this function simply returns 0. * - * The rehashing process ensures, that the number of buckets is at least + * The rehashing process ensures that the number of buckets is at least * 2.5 times the element count. So there is enough room for additional * elements without the need of another soon rehashing. * @@ -127,8 +123,7 @@ CxMap *cxHashMapCreate( * @retval non-zero if a memory allocation error occurred */ cx_attr_nonnull -cx_attr_export -int cxMapRehash(CxMap *map); +CX_EXPORT int cxMapRehash(CxMap *map); #ifdef __cplusplus diff --git a/ucx/cx/iterator.h b/ucx/cx/iterator.h index 204c4df..4fe304a 100644 --- a/ucx/cx/iterator.h +++ b/ucx/cx/iterator.h @@ -50,7 +50,10 @@ struct cx_iterator_base_s { * True if the iterator points to valid data. */ bool (*valid)(const void *); - + /** + * Original implementation in case the function needs to be wrapped. + */ + bool (*valid_impl)(const void *); /** * Returns a pointer to the current element. * @@ -62,17 +65,20 @@ struct cx_iterator_base_s { * Original implementation in case the function needs to be wrapped. */ void *(*current_impl)(const void *); - /** * Advances the iterator. * * When valid returns false, the behavior of this function is undefined. */ void (*next)(void *); + /** + * Original implementation in case the function needs to be wrapped. + */ + void (*next_impl)(void *); /** * Indicates whether this iterator may remove elements. */ - bool mutating; + bool allow_remove; /** * Internal flag for removing the current element when advancing. */ @@ -108,16 +114,7 @@ struct cx_iterator_s { /** * Handle for the source collection, if any. */ - union { - /** - * Access for mutating iterators. - */ - void *m; - /** - * Access for normal iterators. - */ - const void *c; - } src_handle; + void *src_handle; /** * If the iterator is position-aware, contains the index of the element in the underlying collection. @@ -141,11 +138,11 @@ struct cx_iterator_s { * Iterator type. * * An iterator points to a certain element in a (possibly unbounded) chain of elements. - * Iterators that are based on collections (which have a defined "first" element), are supposed + * Iterators that are based on collections (which have a defined "first" element) are supposed * to be "position-aware", which means that they keep track of the current index within the collection. * * @note Objects that are pointed to by an iterator are always mutable through that iterator. However, - * any concurrent mutation of the collection other than by this iterator makes this iterator invalid, + * any concurrent mutation of the collection other than by this iterator makes this iterator obsolete, * and it must not be used anymore. */ typedef struct cx_iterator_s CxIterator; @@ -178,13 +175,12 @@ typedef struct cx_iterator_s CxIterator; #define cxIteratorNext(iter) (iter).base.next(&iter) /** - * Flags the current element for removal, if this iterator is mutating. - * - * Does nothing for non-mutating iterators. + * Flags the current element for removal if the iterator allows it. * * @param iter the iterator + * @return @c true if removal is allowed, @c false otherwise */ -#define cxIteratorFlagRemoval(iter) (iter).base.remove |= (iter).base.mutating +#define cxIteratorFlagRemoval(iter) ((iter).base.remove = (iter).base.allow_remove) /** * Obtains a reference to an arbitrary iterator. @@ -210,7 +206,7 @@ for (type elem; cxIteratorValid(iter) && (elem = (type)cxIteratorCurrent(iter)) /** * Creates an iterator for the specified plain array. * - * The @p array can be @c NULL in which case the iterator will be immediately + * The @p array can be @c NULL, in which case the iterator will be immediately * initialized such that #cxIteratorValid() returns @c false. * * This iterator yields the addresses of the array elements. @@ -218,23 +214,6 @@ for (type elem; cxIteratorValid(iter) && (elem = (type)cxIteratorCurrent(iter)) * use cxIteratorPtr() to create an iterator which directly * yields the stored pointers. * - * @param array a pointer to the array (can be @c NULL) - * @param elem_size the size of one array element - * @param elem_count the number of elements in the array - * @return an iterator for the specified array - * @see cxIteratorPtr() - */ -cx_attr_nodiscard -cx_attr_export -CxIterator cxIterator( - const void *array, - size_t elem_size, - size_t elem_count -); - -/** - * Creates a mutating iterator for the specified plain array. - * * While the iterator is in use, the array may only be altered by removing * elements through #cxIteratorFlagRemoval(). Every other change to the array * will bring this iterator to an undefined state. @@ -244,67 +223,45 @@ CxIterator cxIterator( * moving all subsequent elements by one. Usually, when the order of elements is * not important, this parameter should be set to @c false. * - * The @p array can be @c NULL in which case the iterator will be immediately - * initialized such that #cxIteratorValid() returns @c false. - * - * * @param array a pointer to the array (can be @c NULL) * @param elem_size the size of one array element * @param elem_count the number of elements in the array * @param remove_keeps_order @c true if the order of elements must be preserved * when removing an element * @return an iterator for the specified array + * @see cxIteratorPtr() */ cx_attr_nodiscard -cx_attr_export -CxIterator cxMutIterator( - void *array, - size_t elem_size, - size_t elem_count, - bool remove_keeps_order -); +CX_EXPORT CxIterator cxIterator(const void *array, + size_t elem_size, size_t elem_count, bool remove_keeps_order); /** * Creates an iterator for the specified plain pointer array. * * This iterator assumes that every element in the array is a pointer - * and yields exactly those pointers during iteration (while in contrast - * an iterator created with cxIterator() would return the addresses - * of those pointers within the array). + * and yields exactly those pointers during iteration (on the other + * hand, an iterator created with cxIterator() would return the + * addresses of those pointers within the array). * - * @param array a pointer to the array (can be @c NULL) - * @param elem_count the number of elements in the array - * @return an iterator for the specified array - * @see cxIterator() - */ -cx_attr_nodiscard -cx_attr_export -CxIterator cxIteratorPtr( - const void *array, - size_t elem_count -); - -/** - * Creates a mutating iterator for the specified plain pointer array. + * While the iterator is in use, the array may only be altered by removing + * elements through #cxIteratorFlagRemoval(). Every other change to the array + * will bring this iterator to an undefined state. * - * This is the mutating variant of cxIteratorPtr(). See also - * cxMutIterator(). + * When @p remove_keeps_order is set to @c false, removing an element will only + * move the last element to the position of the removed element, instead of + * moving all subsequent elements by one. Usually, when the order of elements is + * not important, this parameter should be set to @c false. * * @param array a pointer to the array (can be @c NULL) * @param elem_count the number of elements in the array * @param remove_keeps_order @c true if the order of elements must be preserved * when removing an element * @return an iterator for the specified array - * @see cxMutIterator() - * @see cxIteratorPtr() + * @see cxIterator() */ cx_attr_nodiscard -cx_attr_export -CxIterator cxMutIteratorPtr( - void *array, - size_t elem_count, - bool remove_keeps_order -); +CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count, + bool remove_keeps_order); #ifdef __cplusplus } // extern "C" diff --git a/ucx/cx/json.h b/ucx/cx/json.h index ee09ec1..8856ad4 100644 --- a/ucx/cx/json.h +++ b/ucx/cx/json.h @@ -90,11 +90,11 @@ enum cx_json_token_type { */ CX_JSON_TOKEN_STRING, /** - * A number token that can be represented as integer. + * A number token that can be represented as an integer. */ CX_JSON_TOKEN_INTEGER, /** - * A number token that cannot be represented as integer. + * A number token that cannot be represented as an integer. */ CX_JSON_TOKEN_NUMBER, /** @@ -196,11 +196,11 @@ typedef struct cx_json_object_s CxJsonObject; */ typedef struct cx_mutstr_s CxJsonString; /** - * Type alias for a number that can be represented as 64-bit signed integer. + * Type alias for a number that can be represented as a 64-bit signed integer. */ typedef int64_t CxJsonInteger; /** - * Type alias for number that is not an integer. + * Type alias for a number that is not an integer. */ typedef double CxJsonNumber; /** @@ -272,27 +272,27 @@ struct cx_json_value_s { */ union { /** - * The array data if type is #CX_JSON_ARRAY. + * The array data if the type is #CX_JSON_ARRAY. */ CxJsonArray array; /** - * The object data if type is #CX_JSON_OBJECT. + * The object data if the type is #CX_JSON_OBJECT. */ CxJsonObject object; /** - * The string data if type is #CX_JSON_STRING. + * The string data if the type is #CX_JSON_STRING. */ CxJsonString string; /** - * The integer if type is #CX_JSON_INTEGER. + * The integer if the type is #CX_JSON_INTEGER. */ CxJsonInteger integer; /** - * The number if type is #CX_JSON_NUMBER. + * The number if the type is #CX_JSON_NUMBER. */ CxJsonNumber number; /** - * The literal type if type is #CX_JSON_LITERAL. + * The literal type if the type is #CX_JSON_LITERAL. */ CxJsonLiteral literal; } value; @@ -377,7 +377,7 @@ struct cx_json_s { }; /** - * Status codes for the json interface. + * Status codes for the JSON interface. */ enum cx_json_status { /** @@ -391,7 +391,7 @@ enum cx_json_status { /** * The input ends unexpectedly. * - * Refill the buffer with cxJsonFill() to complete the json data. + * Refill the buffer with cxJsonFill() to complete the JSON data. */ CX_JSON_INCOMPLETE_DATA, /** @@ -400,7 +400,7 @@ enum cx_json_status { * You can use this enumerator to check for all "good" status results * by checking if the status is less than @c CX_JSON_OK. * - * A "good" status means, that you can refill data and continue parsing. + * A "good" status means that you can refill data and continue parsing. */ CX_JSON_OK, /** @@ -412,7 +412,7 @@ enum cx_json_status { */ CX_JSON_BUFFER_ALLOC_FAILED, /** - * Allocating memory for a json value failed. + * Allocating memory for a JSON value failed. */ CX_JSON_VALUE_ALLOC_FAILED, /** @@ -426,7 +426,7 @@ enum cx_json_status { }; /** - * Typedef for the json status enum. + * Typedef for the JSON status enum. */ typedef enum cx_json_status CxJsonStatus; @@ -445,7 +445,7 @@ struct cx_json_writer_s { /** * The maximum number of fractional digits in a number value. * The default value is 6 and values larger than 15 are reduced to 15. - * Note, that the actual number of digits may be lower, depending on the concrete number. + * Note that the actual number of digits may be lower, depending on the concrete number. */ uint8_t frac_max_digits; /** @@ -465,7 +465,7 @@ struct cx_json_writer_s { }; /** - * Typedef for the json writer. + * Typedef for the JSON writer. */ typedef struct cx_json_writer_s CxJsonWriter; @@ -475,8 +475,7 @@ typedef struct cx_json_writer_s CxJsonWriter; * @return new JSON writer settings */ cx_attr_nodiscard -cx_attr_export -CxJsonWriter cxJsonWriterCompact(void); +CX_EXPORT CxJsonWriter cxJsonWriterCompact(void); /** * Creates a default writer configuration for pretty output. @@ -485,8 +484,7 @@ CxJsonWriter cxJsonWriterCompact(void); * @return new JSON writer settings */ cx_attr_nodiscard -cx_attr_export -CxJsonWriter cxJsonWriterPretty(bool use_spaces); +CX_EXPORT CxJsonWriter cxJsonWriterPretty(bool use_spaces); /** * Writes a JSON value to a buffer or stream. @@ -496,9 +494,8 @@ CxJsonWriter cxJsonWriterPretty(bool use_spaces); * that the data is only partially written when an error occurs with no * way to indicate how much data was written. * To avoid this problem, you can use a CxBuffer as @p target which is - * unlikely to fail a write operation and either use the buffer's flush - * feature to relay the data or use the data in the buffer manually to - * write it to the actual target. + * unlikely to fail a write operation. You can, for example, use the buffer's flush + * feature to relay the data. * * @param target the buffer or stream where to write to * @param value the value that shall be written @@ -508,49 +505,38 @@ CxJsonWriter cxJsonWriterPretty(bool use_spaces); * @retval non-zero when no or not all data could be written */ cx_attr_nonnull_arg(1, 2, 3) -cx_attr_export -int cxJsonWrite( - void* target, - const CxJsonValue* value, - cx_write_func wfunc, - const CxJsonWriter* settings -); +CX_EXPORT int cxJsonWrite(void* target, const CxJsonValue* value, + cx_write_func wfunc, const CxJsonWriter* settings); /** - * Initializes the json interface. + * Initializes the JSON interface. * - * @param json the json interface + * @param json the JSON interface * @param allocator the allocator that shall be used for the produced values * @see cxJsonDestroy() */ cx_attr_nonnull_arg(1) -cx_attr_export -void cxJsonInit(CxJson *json, const CxAllocator *allocator); +CX_EXPORT void cxJsonInit(CxJson *json, const CxAllocator *allocator); /** - * Destroys the json interface. + * Destroys the JSON interface. * - * @param json the json interface + * @param json the JSON interface * @see cxJsonInit() */ cx_attr_nonnull -cx_attr_export -void cxJsonDestroy(CxJson *json); +CX_EXPORT void cxJsonDestroy(CxJson *json); /** - * Destroys and re-initializes the json interface. + * Destroys and re-initializes the JSON interface. * - * You might want to use this, to reset the parser after + * You might want to use this to reset the parser after * encountering a syntax error. * - * @param json the json interface + * @param json the JSON interface */ cx_attr_nonnull -static inline void cxJsonReset(CxJson *json) { - const CxAllocator *allocator = json->allocator; - cxJsonDestroy(json); - cxJsonInit(json, allocator); -} +CX_EXPORT void cxJsonReset(CxJson *json); /** * Fills the input buffer. @@ -563,48 +549,30 @@ static inline void cxJsonReset(CxJson *json) { * the additional data is appended - inevitably leading to * an allocation of a new buffer and copying the previous contents. * - * @param json the json interface + * @param json the JSON interface * @param buf the source buffer * @param len the length of the source buffer * @retval zero success * @retval non-zero internal allocation error * @see cxJsonFill() */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -int cxJsonFilln(CxJson *json, const char *buf, size_t len); - -#ifdef __cplusplus -} // extern "C" +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len); -cx_attr_nonnull -static inline int cxJsonFill( - CxJson *json, - cxstring str -) { - return cxJsonFilln(json, str.ptr, str.length); -} +/** + * Internal function, do not use. + * + * @param json the JSON interface + * @param str the string + * @retval zero success + * @retval non-zero internal allocation error + */ cx_attr_nonnull -static inline int cxJsonFill( - CxJson *json, - cxmutstr str -) { +CX_INLINE int cx_json_fill(CxJson *json, cxstring str) { return cxJsonFilln(json, str.ptr, str.length); } -cx_attr_nonnull -cx_attr_cstr_arg(2) -static inline int cxJsonFill( - CxJson *json, - const char *str -) { - return cxJsonFilln(json, str, strlen(str)); -} - -extern "C" { -#else // __cplusplus /** * Fills the input buffer. * @@ -616,53 +584,13 @@ extern "C" { * the additional data is appended - inevitably leading to * an allocation of a new buffer and copying the previous contents. * - * @param json the json interface + * @param json the JSON interface * @param str the source string * @retval zero success * @retval non-zero internal allocation error * @see cxJsonFilln() */ -#define cxJsonFill(json, str) _Generic((str), \ - cxstring: cx_json_fill_cxstr, \ - cxmutstr: cx_json_fill_mutstr, \ - char*: cx_json_fill_str, \ - const char*: cx_json_fill_str) \ - (json, str) - -/** - * @copydoc cxJsonFill() - */ -cx_attr_nonnull -static inline int cx_json_fill_cxstr( - CxJson *json, - cxstring str -) { - return cxJsonFilln(json, str.ptr, str.length); -} - -/** - * @copydoc cxJsonFill() - */ -cx_attr_nonnull -static inline int cx_json_fill_mutstr( - CxJson *json, - cxmutstr str -) { - return cxJsonFilln(json, str.ptr, str.length); -} - -/** - * @copydoc cxJsonFill() - */ -cx_attr_nonnull -cx_attr_cstr_arg(2) -static inline int cx_json_fill_str( - CxJson *json, - const char *str -) { - return cxJsonFilln(json, str, strlen(str)); -} -#endif +#define cxJsonFill(json, str) cx_json_fill(json, cx_strcast(str)) /** * Creates a new (empty) JSON object. @@ -673,8 +601,7 @@ static inline int cx_json_fill_str( * @see cxJsonArrAddValues() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); +CX_EXPORT CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); /** * Creates a new (empty) JSON array. @@ -685,8 +612,7 @@ CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); * @see cxJsonArrAddValues() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); +CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); /** * Creates a new JSON number value. @@ -698,8 +624,7 @@ CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); * @see cxJsonArrAddNumbers() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num); +CX_EXPORT CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num); /** * Creates a new JSON number value based on an integer. @@ -711,8 +636,7 @@ CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num); * @see cxJsonArrAddIntegers() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num); +CX_EXPORT CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num); /** * Creates a new JSON string. @@ -724,11 +648,8 @@ CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num); * @see cxJsonObjPutString() * @see cxJsonArrAddStrings() */ -cx_attr_nodiscard -cx_attr_nonnull_arg(2) -cx_attr_cstr_arg(2) -cx_attr_export -CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str); +cx_attr_nodiscard cx_attr_nonnull_arg(2) cx_attr_cstr_arg(2) +CX_EXPORT CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str); /** * Creates a new JSON string. @@ -741,8 +662,7 @@ CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str); * @see cxJsonArrAddCxStrings() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str); +CX_EXPORT CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str); /** * Creates a new JSON literal. @@ -754,8 +674,7 @@ CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str); * @see cxJsonArrAddLiterals() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit); +CX_EXPORT CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit); /** * Adds number values to a JSON array. @@ -766,10 +685,8 @@ CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit * @retval zero success * @retval non-zero allocation failure */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count); /** * Adds number values, of which all are integers, to a JSON array. @@ -780,10 +697,8 @@ int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count); * @retval zero success * @retval non-zero allocation failure */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count); /** * Adds strings to a JSON array. @@ -797,10 +712,8 @@ int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count); * @retval non-zero allocation failure * @see cxJsonArrAddCxStrings() */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count); /** * Adds strings to a JSON array. @@ -814,10 +727,8 @@ int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count); * @retval non-zero allocation failure * @see cxJsonArrAddStrings() */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count); /** * Adds literals to a JSON array. @@ -828,10 +739,8 @@ int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count); * @retval zero success * @retval non-zero allocation failure */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count); /** * Add arbitrary values to a JSON array. @@ -845,10 +754,8 @@ int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t coun * @retval zero success * @retval non-zero allocation failure */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count); /** * Adds or replaces a value within a JSON object. @@ -865,8 +772,7 @@ int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count); * @retval non-zero allocation failure */ cx_attr_nonnull -cx_attr_export -int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child); +CX_EXPORT int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child); /** * Creates a new JSON object and adds it to an existing object. @@ -878,8 +784,7 @@ int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child); * @see cxJsonCreateObj() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); +CX_EXPORT CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); /** * Creates a new JSON array and adds it to an object. @@ -891,8 +796,7 @@ CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); * @see cxJsonCreateArr() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); +CX_EXPORT CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); /** * Creates a new JSON number and adds it to an object. @@ -905,8 +809,7 @@ CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); * @see cxJsonCreateNumber() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); +CX_EXPORT CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); /** * Creates a new JSON number, based on an integer, and adds it to an object. @@ -919,8 +822,7 @@ CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); * @see cxJsonCreateInteger() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); +CX_EXPORT CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); /** * Creates a new JSON string and adds it to an object. @@ -934,10 +836,8 @@ CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); * @see cxJsonObjPut() * @see cxJsonCreateString() */ -cx_attr_nonnull -cx_attr_cstr_arg(3) -cx_attr_export -CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str); +cx_attr_nonnull cx_attr_cstr_arg(3) +CX_EXPORT CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str); /** * Creates a new JSON string and adds it to an object. @@ -952,8 +852,7 @@ CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str * @see cxJsonCreateCxString() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str); +CX_EXPORT CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str); /** * Creates a new JSON literal and adds it to an object. @@ -966,22 +865,20 @@ CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str) * @see cxJsonCreateLiteral() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit); +CX_EXPORT CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit); /** * Recursively deallocates the memory of a JSON value. * * @remark The type of each deallocated value will be changed - * to #CX_JSON_NOTHING and values of such type will be skipped - * by the de-allocation. That means, this function protects + * to #CX_JSON_NOTHING, and values of such a type will be skipped + * by the deallocation. That means this function protects * you from double-frees when you are accidentally freeing * a nested value and then the parent value (or vice versa). * * @param value the value */ -cx_attr_export -void cxJsonValueFree(CxJsonValue *value); +CX_EXPORT void cxJsonValueFree(CxJsonValue *value); /** * Tries to obtain the next JSON value. @@ -993,7 +890,7 @@ void cxJsonValueFree(CxJsonValue *value); * add the missing data with another invocation of cxJsonFill() * and then repeat the call to cxJsonNext(). * - * @param json the json interface + * @param json the JSON interface * @param value a pointer where the next value shall be stored * @retval CX_JSON_NO_ERROR successfully retrieve the @p value * @retval CX_JSON_NO_DATA there is no (more) data in the buffer to read from @@ -1005,10 +902,8 @@ void cxJsonValueFree(CxJsonValue *value); * @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number * @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error */ -cx_attr_nonnull -cx_attr_access_w(2) -cx_attr_export -CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); +cx_attr_nonnull cx_attr_access_w(2) +CX_EXPORT CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); /** * Checks if the specified value is a JSON object. @@ -1018,7 +913,7 @@ CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); * @retval false otherwise */ cx_attr_nonnull -static inline bool cxJsonIsObject(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsObject(const CxJsonValue *value) { return value->type == CX_JSON_OBJECT; } @@ -1030,7 +925,7 @@ static inline bool cxJsonIsObject(const CxJsonValue *value) { * @retval false otherwise */ cx_attr_nonnull -static inline bool cxJsonIsArray(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsArray(const CxJsonValue *value) { return value->type == CX_JSON_ARRAY; } @@ -1042,14 +937,14 @@ static inline bool cxJsonIsArray(const CxJsonValue *value) { * @retval false otherwise */ cx_attr_nonnull -static inline bool cxJsonIsString(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsString(const CxJsonValue *value) { return value->type == CX_JSON_STRING; } /** * Checks if the specified value is a JSON number. * - * This function will return true for both floating point and + * This function will return true for both floating-point and * integer numbers. * * @param value a pointer to the value @@ -1058,7 +953,7 @@ static inline bool cxJsonIsString(const CxJsonValue *value) { * @see cxJsonIsInteger() */ cx_attr_nonnull -static inline bool cxJsonIsNumber(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsNumber(const CxJsonValue *value) { return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER; } @@ -1071,7 +966,7 @@ static inline bool cxJsonIsNumber(const CxJsonValue *value) { * @see cxJsonIsNumber() */ cx_attr_nonnull -static inline bool cxJsonIsInteger(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsInteger(const CxJsonValue *value) { return value->type == CX_JSON_INTEGER; } @@ -1088,7 +983,7 @@ static inline bool cxJsonIsInteger(const CxJsonValue *value) { * @see cxJsonIsNull() */ cx_attr_nonnull -static inline bool cxJsonIsLiteral(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsLiteral(const CxJsonValue *value) { return value->type == CX_JSON_LITERAL; } @@ -1102,14 +997,14 @@ static inline bool cxJsonIsLiteral(const CxJsonValue *value) { * @see cxJsonIsFalse() */ cx_attr_nonnull -static inline bool cxJsonIsBool(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL; } /** * Checks if the specified value is @c true. * - * @remark Be advised, that this is not the same as + * @remark Be advised that this is different from * testing @c !cxJsonIsFalse(v). * * @param value a pointer to the value @@ -1119,14 +1014,14 @@ static inline bool cxJsonIsBool(const CxJsonValue *value) { * @see cxJsonIsFalse() */ cx_attr_nonnull -static inline bool cxJsonIsTrue(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE; } /** * Checks if the specified value is @c false. * - * @remark Be advised, that this is not the same as + * @remark Be advised that this is different from * testing @c !cxJsonIsTrue(v). * * @param value a pointer to the value @@ -1136,7 +1031,7 @@ static inline bool cxJsonIsTrue(const CxJsonValue *value) { * @see cxJsonIsTrue() */ cx_attr_nonnull -static inline bool cxJsonIsFalse(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE; } @@ -1149,7 +1044,7 @@ static inline bool cxJsonIsFalse(const CxJsonValue *value) { * @see cxJsonIsLiteral() */ cx_attr_nonnull -static inline bool cxJsonIsNull(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL; } @@ -1162,11 +1057,8 @@ static inline bool cxJsonIsNull(const CxJsonValue *value) { * @return the value represented as C string * @see cxJsonIsString() */ -cx_attr_nonnull -cx_attr_returns_nonnull -static inline char *cxJsonAsString(const CxJsonValue *value) { - return value->value.string.ptr; -} +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT char *cxJsonAsString(const CxJsonValue *value); /** * Obtains a UCX string from the given JSON value. @@ -1178,9 +1070,7 @@ static inline char *cxJsonAsString(const CxJsonValue *value) { * @see cxJsonIsString() */ cx_attr_nonnull -static inline cxstring cxJsonAsCxString(const CxJsonValue *value) { - return cx_strcast(value->value.string); -} +CX_EXPORT cxstring cxJsonAsCxString(const CxJsonValue *value); /** * Obtains a mutable UCX string from the given JSON value. @@ -1192,12 +1082,10 @@ static inline cxstring cxJsonAsCxString(const CxJsonValue *value) { * @see cxJsonIsString() */ cx_attr_nonnull -static inline cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { - return value->value.string; -} +CX_EXPORT cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value); /** - * Obtains a double-precision floating point value from the given JSON value. + * Obtains a double-precision floating-point value from the given JSON value. * * If the @p value is not a JSON number, the behavior is undefined. * @@ -1206,13 +1094,7 @@ static inline cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { * @see cxJsonIsNumber() */ cx_attr_nonnull -static inline double cxJsonAsDouble(const CxJsonValue *value) { - if (value->type == CX_JSON_INTEGER) { - return (double) value->value.integer; - } else { - return value->value.number; - } -} +CX_EXPORT double cxJsonAsDouble(const CxJsonValue *value); /** * Obtains a 64-bit signed integer from the given JSON value. @@ -1227,13 +1109,7 @@ static inline double cxJsonAsDouble(const CxJsonValue *value) { * @see cxJsonIsInteger() */ cx_attr_nonnull -static inline int64_t cxJsonAsInteger(const CxJsonValue *value) { - if (value->type == CX_JSON_INTEGER) { - return value->value.integer; - } else { - return (int64_t) value->value.number; - } -} +CX_EXPORT int64_t cxJsonAsInteger(const CxJsonValue *value); /** * Obtains a Boolean value from the given JSON value. @@ -1246,7 +1122,7 @@ static inline int64_t cxJsonAsInteger(const CxJsonValue *value) { * @see cxJsonIsLiteral() */ cx_attr_nonnull -static inline bool cxJsonAsBool(const CxJsonValue *value) { +CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) { return value->value.literal == CX_JSON_TRUE; } @@ -1260,7 +1136,7 @@ static inline bool cxJsonAsBool(const CxJsonValue *value) { * @see cxJsonIsArray() */ cx_attr_nonnull -static inline size_t cxJsonArrSize(const CxJsonValue *value) { +CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) { return value->value.array.array_size; } @@ -1278,10 +1154,24 @@ static inline size_t cxJsonArrSize(const CxJsonValue *value) { * @return the value at the specified index * @see cxJsonIsArray() */ +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); + +/** + * Removes an element from a JSON array. + * + * If the @p value is not a JSON array, the behavior is undefined. + * + * This function, in contrast to cxJsonArrayGet(), returns @c NULL + * when the index is out of bounds. + * + * @param value the JSON value + * @param index the index in the array + * @return the removed value from the specified index or @c NULL when the index was out of bounds + * @see cxJsonIsArray() + */ cx_attr_nonnull -cx_attr_returns_nonnull -cx_attr_export -CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); +CX_EXPORT CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index); /** * Returns an iterator over the JSON array elements. @@ -1294,10 +1184,8 @@ CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); * @return an iterator over the array elements * @see cxJsonIsArray() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxIterator cxJsonArrIter(const CxJsonValue *value); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value); /** * Returns an iterator over the JSON object members. @@ -1311,36 +1199,18 @@ CxIterator cxJsonArrIter(const CxJsonValue *value); * @return an iterator over the object members * @see cxJsonIsObject() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxIterator cxJsonObjIter(const CxJsonValue *value); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxIterator cxJsonObjIter(const CxJsonValue *value); /** - * @copydoc cxJsonObjGet() + * Internal function, do not use. + * @param value the JSON object + * @param name the key to look up + * @return the value corresponding to the key */ -cx_attr_nonnull -cx_attr_returns_nonnull -cx_attr_export -CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name); +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name); -#ifdef __cplusplus -} // extern "C" - -static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) { - return cx_json_obj_get_cxstr(value, name); -} - -static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) { - return cx_json_obj_get_cxstr(value, cx_strcast(name)); -} - -static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) { - return cx_json_obj_get_cxstr(value, cx_str(name)); -} - -extern "C" { -#else /** * Returns a value corresponding to a key in a JSON object. * @@ -1355,32 +1225,31 @@ extern "C" { * @return the value corresponding to the key * @see cxJsonIsObject() */ -#define cxJsonObjGet(value, name) _Generic((name), \ - cxstring: cx_json_obj_get_cxstr, \ - cxmutstr: cx_json_obj_get_mutstr, \ - char*: cx_json_obj_get_str, \ - const char*: cx_json_obj_get_str) \ - (value, name) +#define cxJsonObjGet(value, name) cx_json_obj_get(value, cx_strcast(name)) /** - * @copydoc cxJsonObjGet() + * Internal function, do not use. + * @param value the JSON object + * @param name the key to look up + * @return the value corresponding to the key or @c NULL when the key is not part of the object */ cx_attr_nonnull -cx_attr_returns_nonnull -static inline CxJsonValue *cx_json_obj_get_mutstr(const CxJsonValue *value, cxmutstr name) { - return cx_json_obj_get_cxstr(value, cx_strcast(name)); -} +CX_EXPORT CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name); /** - * @copydoc cxJsonObjGet() + * Removes and returns a value corresponding to a key in a JSON object. + * + * If the @p value is not a JSON object, the behavior is undefined. + * + * This function, in contrast to cxJsonObjGet() returns @c NULL when the + * object does not contain @p name. + * + * @param value the JSON object + * @param name the key to look up + * @return the value corresponding to the key or @c NULL when the key is not part of the object + * @see cxJsonIsObject() */ -cx_attr_nonnull -cx_attr_returns_nonnull -cx_attr_cstr_arg(2) -static inline CxJsonValue *cx_json_obj_get_str(const CxJsonValue *value, const char *name) { - return cx_json_obj_get_cxstr(value, cx_str(name)); -} -#endif +#define cxJsonObjRemove(value, name) cx_json_obj_remove(value, cx_strcast(name)) #ifdef __cplusplus } diff --git a/ucx/cx/kv_list.h b/ucx/cx/kv_list.h new file mode 100644 index 0000000..c66e46c --- /dev/null +++ b/ucx/cx/kv_list.h @@ -0,0 +1,260 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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 kv_list.h + * @brief Linked list implementation with key/value-lookup. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License + */ + +#ifndef UCX_KV_LIST_H +#define UCX_KV_LIST_H + +#include "common.h" +#include "list.h" +#include "map.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each. + * + * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of + * copies of the added elements, and the compare function will be automatically set + * to cx_cmp_ptr() if none is given. + * + * After creating the list, it can also be used as a map after converting the pointer + * to a CxMap pointer with cxKvListAsMap(). + * When you want to use the list interface again, you can also convert the map pointer back + * with cxKvListAsList(). + * + * @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, and the list is not storing pointers, sort and find + * functions will not work) + * @param elem_size the size of each element in bytes + * @return the created list + * @see cxKvListAsMap() + * @see cxKvListAsList() + */ +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) +CX_EXPORT CxList *cxKvListCreate(const CxAllocator *allocator, + cx_compare_func comparator, size_t elem_size); + +/** + * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each. + * + * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of + * copies of the added elements, and the compare function will be automatically set + * to cx_cmp_ptr() if none is given. + * + * This function creates the list with cxKvListCreate() and immediately applies + * cxKvListAsMap(). If you want to use the returned object as a list, you can call + * cxKvListAsList() later. + * + * @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, and the list is not storing pointers, sort and find + * functions will not work) + * @param elem_size the size of each element in bytes + * @return the created list wrapped into the CxMap interface + * @see cxKvListAsMap() + * @see cxKvListAsList() + */ +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1) +CX_EXPORT CxMap *cxKvListCreateAsMap(const CxAllocator *allocator, + cx_compare_func comparator, size_t elem_size); + +/** + * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each. + * + * The list will use cxDefaultAllocator and no comparator function. If you want + * to call functions that need a comparator, you must either set one immediately + * after list creation or use cxKvListCreate(). + * + * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of + * copies of the added elements, and the compare function will be automatically set + * to cx_cmp_ptr(). + * + * After creating the list, it can also be used as a map after converting the pointer + * to a CxMap pointer with cxKvListAsMap(). + * When you want to use the list interface again, you can also convert the map pointer back + * with cxKvListAsList(). + * + * @param elem_size (@c size_t) the size of each element in bytes + * @return (@c CxList*) the created list + * @see cxKvListAsMap() + * @see cxKvListAsList() + */ +#define cxKvListCreateSimple(elem_size) cxKvListCreate(NULL, NULL, elem_size) + +/** + * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each. + * + * The list will use cxDefaultAllocator and no comparator function. If you want + * to call functions that need a comparator, you must either set one immediately + * after list creation or use cxKvListCreate(). + * + * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of + * copies of the added elements, and the compare function will be automatically set + * to cx_cmp_ptr(). + * + * This macro behaves as if the list was created with cxKvListCreateSimple() and + * immediately followed up by cxKvListAsMap(). + * If you want to use the returned object as a list, you can call cxKvListAsList() later. + * + * @param elem_size (@c size_t) the size of each element in bytes + * @return (@c CxMap*) the created list wrapped into the CxMap interface + * @see cxKvListAsMap() + * @see cxKvListAsList() + */ +#define cxKvListCreateAsMapSimple(elem_size) cxKvListCreateAsMap(NULL, NULL, elem_size) + +/** + * Converts a map pointer belonging to a key-value-List back to the original list pointer. + * + * @param map a map pointer that was returned by a call to cxKvListAsMap() + * @return the original list pointer + */ +cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT CxList *cxKvListAsList(CxMap *map); + +/** + * Converts a map pointer belonging to a key-value-List back to the original list pointer. + * + * @param list a list created by cxKvListCreate() or cxKvListCreateSimple() + * @return a map pointer that lets you use the list as if it was a map + */ +cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT CxMap *cxKvListAsMap(CxList *list); + +/** + * Sets or updates the key of a list item. + * + * This is, for example, useful when you have inserted an element using the CxList interface, + * and now you want to associate this element with a key. + * + * @param list the list + * @param index the index of the element in the list + * @param key the key + * @retval zero success + * @retval non-zero memory allocation failure or the index is out of bounds + * @see cxKvListSetKey() + */ +cx_attr_nonnull +CX_EXPORT int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key); + +/** + * Inserts an item into the list at the specified index and associates it with the specified key. + * + * @param list the list + * @param index the index the inserted element shall have + * @param key the key + * @param value the value + * @retval zero success + * @retval non-zero memory allocation failure or the index is out of bounds + * @see cxKvListInsert() + */ +cx_attr_nonnull +CX_EXPORT int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value); + +/** + * Sets or updates the key of a list item. + * + * This is, for example, useful when you have inserted an element using the CxList interface, + * and now you want to associate this element with a key. + * + * @param list (@c CxList*) the list + * @param index (@c size_t) the index of the element in the list + * @param key (any supported key type) the key + * @retval zero success + * @retval non-zero memory allocation failure or the index is out of bounds + * @see CX_HASH_KEY() + */ +#define cxKvListSetKey(list, index, key) cx_kv_list_set_key(list, index, CX_HASH_KEY(key)) + +/** + * Inserts an item into the list at the specified index and associates it with the specified key. + * + * @param list (@c CxList*) the list + * @param index (@c size_t) the index the inserted element shall have + * @param key (any supported key type) the key + * @param value (@c void*) the value + * @retval zero success + * @retval non-zero memory allocation failure or the index is out of bounds + * @see CX_HASH_KEY() + */ +#define cxKvListInsert(list, index, key, value) cx_kv_list_insert(list, index, CX_HASH_KEY(key), value) + + +/** + * Removes the key of a list item. + * + * This can be useful if you want to explicitly remove an item from the lookup map. + * + * If no key is associated with the item, nothing happens, and this function returns zero. + * + * @param list the list + * @param index the index of the element in the list + * @retval zero success + * @retval non-zero the index is out of bounds + */ +cx_attr_nonnull +CX_EXPORT int cxKvListRemoveKey(CxList *list, size_t index); + +/** + * Returns the key of a list item. + * + * @param list the list + * @param index the index of the element in the list + * @return a pointer to the key or @c NULL when the index is out of bounds or the item does not have a key + */ +cx_attr_nonnull +CX_EXPORT const CxHashKey *cxKvListGetKey(CxList *list, size_t index); + +/** + * Adds an item into the list and associates it with the specified key. + * + * @param list (@c CxList*) the list + * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key + * @param value (@c void*) the value + * @retval zero success + * @retval non-zero memory allocation failure + */ +#define cxKvListAdd(list, key, value) cxKvListInsert(list, (list)->collection.size, key, value) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_KV_LIST_H diff --git a/ucx/cx/linked_list.h b/ucx/cx/linked_list.h index 3da2cae..996dc58 100644 --- a/ucx/cx/linked_list.h +++ b/ucx/cx/linked_list.h @@ -43,12 +43,44 @@ extern "C" { #endif +/** + * Metadata for a linked list. + */ +typedef struct cx_linked_list_s { + /** Base members. */ + struct cx_list_s base; + /** + * Location of the prev pointer (mandatory). + */ + off_t loc_prev; + /** + * Location of the next pointer (mandatory). + */ + off_t loc_next; + /** + * Location of the payload (mandatory). + */ + off_t loc_data; + /** + * Additional bytes to allocate @em behind the payload (e.g. for metadata). + */ + size_t extra_data_len; + /** + * Pointer to the first node. + */ + void *begin; + /** + * Pointer to the last node. + */ + void *end; +} cx_linked_list; + /** * Allocates a linked list for storing elements with @p elem_size bytes each. * * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of - * copies of the added elements and the compare function will be automatically set - * to cx_cmp_ptr(), if none is given. + * copies of the added elements, 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) @@ -58,15 +90,9 @@ extern "C" { * @param elem_size the size of each element in bytes * @return the created list */ -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxListFree, 1) -cx_attr_export -CxList *cxLinkedListCreate( - const CxAllocator *allocator, - cx_compare_func comparator, - size_t elem_size -); +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) +CX_EXPORT CxList *cxLinkedListCreate(const CxAllocator *allocator, + cx_compare_func comparator, size_t elem_size); /** * Allocates a linked list for storing elements with @p elem_size bytes each. @@ -76,25 +102,25 @@ CxList *cxLinkedListCreate( * after list creation or use cxLinkedListCreate(). * * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of - * copies of the added elements and the compare function will be automatically set + * copies of the added elements, and the compare function will be automatically set * to cx_cmp_ptr(). * * @param elem_size (@c size_t) the size of each element in bytes * @return (@c CxList*) the created list */ #define cxLinkedListCreateSimple(elem_size) \ - cxLinkedListCreate(NULL, NULL, elem_size) + cxLinkedListCreate(NULL, NULL, elem_size) /** * Finds the node at a certain index. * * This function can be used to start at an arbitrary position within the list. - * If the search index is large than the start index, @p loc_advance must denote - * the location of some sort of @c next pointer (i.e. a pointer to the next node). + * If the search index is larger than the start index, @p loc_advance must denote + * the location of a @c next pointer (i.e., a pointer to the next node). * But it is also possible that the search index is smaller than the start index - * (e.g. in cases where traversing a list backwards is faster) in which case - * @p loc_advance must denote the location of some sort of @c prev pointer - * (i.e. a pointer to the previous node). + * (e.g., in cases where traversing a list backwards is faster). + * In that case @p loc_advance must denote the location of a @c prev pointer + * (i.e., a pointer to the previous node). * * @param start a pointer to the start node * @param start_index the start index @@ -102,15 +128,9 @@ CxList *cxLinkedListCreate( * @param index the search index * @return the node found at the specified index */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -void *cx_linked_list_at( - const void *start, - size_t start_index, - ptrdiff_t loc_advance, - size_t index -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT void *cx_linked_list_at(const void *start,size_t start_index, + ptrdiff_t loc_advance, size_t index); /** * Finds the node containing an element within a linked list. @@ -122,18 +142,12 @@ void *cx_linked_list_at( * @param elem a pointer to the element to find * @param found_index an optional pointer where the index of the found node * (given that @p start has index 0) is stored - * @return the index of the element, if found - unspecified if not found + * @return a pointer to the found node or @c NULL if no matching node was found */ cx_attr_nonnull_arg(1, 4, 5) -cx_attr_export -void *cx_linked_list_find( - const void *start, - ptrdiff_t loc_advance, - ptrdiff_t loc_data, - cx_compare_func cmp_func, - const void *elem, - size_t *found_index -); +CX_EXPORT void *cx_linked_list_find(const void *start, ptrdiff_t loc_advance, + ptrdiff_t loc_data, cx_compare_func cmp_func, const void *elem, + size_t *found_index); /** * Finds the first node in a linked list. @@ -146,13 +160,8 @@ void *cx_linked_list_find( * @param loc_prev the location of the @c prev pointer * @return a pointer to the first node */ -cx_attr_nonnull -cx_attr_returns_nonnull -cx_attr_export -void *cx_linked_list_first( - const void *node, - ptrdiff_t loc_prev -); +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev); /** * Finds the last node in a linked list. @@ -165,13 +174,8 @@ void *cx_linked_list_first( * @param loc_next the location of the @c next pointer * @return a pointer to the last node */ -cx_attr_nonnull -cx_attr_returns_nonnull -cx_attr_export -void *cx_linked_list_last( - const void *node, - ptrdiff_t loc_next -); +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT void *cx_linked_list_last(const void *node, ptrdiff_t loc_next); /** * Finds the predecessor of a node in case it is not linked. @@ -184,16 +188,11 @@ void *cx_linked_list_last( * @return the node or @c NULL if @p node has no predecessor */ cx_attr_nonnull -cx_attr_export -void *cx_linked_list_prev( - const void *begin, - ptrdiff_t loc_next, - const void *node -); +CX_EXPORT void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const void *node); /** * Adds a new node to a linked list. - * The node must not be part of any list already. + * The node must not be part of any list yet. * * @remark One of the pointers @p begin or @p end may be @c NULL, but not both. * @@ -204,18 +203,11 @@ void *cx_linked_list_prev( * @param new_node a pointer to the node that shall be appended */ cx_attr_nonnull_arg(5) -cx_attr_export -void cx_linked_list_add( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *new_node -); +CX_EXPORT void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node); /** * Prepends a new node to a linked list. - * The node must not be part of any list already. + * The node must not be part of any list yet. * * @remark One of the pointers @p begin or @p end may be @c NULL, but not both. * @@ -226,14 +218,7 @@ void cx_linked_list_add( * @param new_node a pointer to the node that shall be prepended */ cx_attr_nonnull_arg(5) -cx_attr_export -void cx_linked_list_prepend( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *new_node -); +CX_EXPORT void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node); /** * Links two nodes. @@ -244,13 +229,7 @@ void cx_linked_list_prepend( * @param loc_next the location of a @c next pointer within your node struct (required) */ cx_attr_nonnull -cx_attr_export -void cx_linked_list_link( - void *left, - void *right, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Unlinks two nodes. @@ -263,17 +242,11 @@ void cx_linked_list_link( * @param loc_next the location of a @c next pointer within your node struct (required) */ cx_attr_nonnull -cx_attr_export -void cx_linked_list_unlink( - void *left, - void *right, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Inserts a new node after a given node of a linked list. - * The new node must not be part of any list already. + * The new node must not be part of any list yet. * * @note If you specify @c NULL as the @p node to insert after, this function needs either the @p begin or * the @p end pointer to determine the start of the list. Then the new node will be prepended to the list. @@ -286,19 +259,12 @@ void cx_linked_list_unlink( * @param new_node a pointer to the node that shall be inserted */ cx_attr_nonnull_arg(6) -cx_attr_export -void cx_linked_list_insert( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *node, - void *new_node -); +CX_EXPORT void cx_linked_list_insert(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *new_node); /** * Inserts a chain of nodes after a given node of a linked list. - * The chain must not be part of any list already. + * The chain must not be part of any list yet. * * If you do not explicitly specify the end of the chain, it will be determined by traversing * the @c next pointer. @@ -317,20 +283,12 @@ void cx_linked_list_insert( * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined) */ cx_attr_nonnull_arg(6) -cx_attr_export -void cx_linked_list_insert_chain( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *node, - void *insert_begin, - void *insert_end -); +CX_EXPORT void cx_linked_list_insert_chain(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *insert_begin, void *insert_end); /** * Inserts a node into a sorted linked list. - * The new node must not be part of any list already. + * The new node must not be part of any list yet. * * If the list starting with the node pointed to by @p begin is not sorted * already, the behavior is undefined. @@ -343,26 +301,19 @@ void cx_linked_list_insert_chain( * @param cmp_func a compare function that will receive the node pointers */ cx_attr_nonnull_arg(1, 5, 6) -cx_attr_export -void cx_linked_list_insert_sorted( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *new_node, - cx_compare_func cmp_func -); +CX_EXPORT void cx_linked_list_insert_sorted(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func); /** * Inserts a chain of nodes into a sorted linked list. - * The chain must not be part of any list already. + * The chain must not be part of any list yet. * * If either the list starting with the node pointed to by @p begin or the list * starting with @p insert_begin is not sorted, the behavior is undefined. * * @attention In contrast to cx_linked_list_insert_chain(), the source chain * will be broken and inserted into the target list so that the resulting list - * will be sorted according to @p cmp_func. That means, each node in the source + * will be sorted according to @p cmp_func. That means each node in the source * chain may be re-linked with nodes from the target list. * * @param begin a pointer to the beginning node pointer (required) @@ -373,20 +324,55 @@ void cx_linked_list_insert_sorted( * @param cmp_func a compare function that will receive the node pointers */ cx_attr_nonnull_arg(1, 5, 6) -cx_attr_export -void cx_linked_list_insert_sorted_chain( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *insert_begin, - cx_compare_func cmp_func -); +CX_EXPORT void cx_linked_list_insert_sorted_chain(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func); + +/** + * Inserts a node into a sorted linked list if no other node with the same value already exists. + * The new node must not be part of any list yet. + * + * If the list starting with the node pointed to by @p begin is not sorted + * already, the behavior is undefined. + * + * @param begin a pointer to the beginning node pointer (required) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) + * @param new_node a pointer to the node that shall be inserted + * @param cmp_func a compare function that will receive the node pointers + * @retval zero when the node was inserted + * @retval non-zero when a node with the same value already exists + */ +cx_attr_nonnull_arg(1, 5, 6) +CX_EXPORT int cx_linked_list_insert_unique(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func); + +/** + * Inserts a chain of nodes into a sorted linked list, avoiding duplicates. + * The chain must not be part of any list yet. + * + * If either the list starting with the node pointed to by @p begin or the list + * starting with @p insert_begin is not sorted, the behavior is undefined. + * + * @attention In contrast to cx_linked_list_insert_sorted(), not all nodes of the + * chain might be added. This function returns a new chain consisting of all the duplicates. + * + * @param begin a pointer to the beginning node pointer (required) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) + * @param insert_begin a pointer to the first node of the chain that shall be inserted + * @param cmp_func a compare function that will receive the node pointers + * @return a pointer to a new chain with all duplicates that were not inserted (or @c NULL when there were no duplicates) + */ +cx_attr_nonnull_arg(1, 5, 6) cx_attr_nodiscard +CX_EXPORT void *cx_linked_list_insert_unique_chain(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func); /** * Removes a chain of nodes from the linked list. * - * If one of the nodes to remove is the beginning (resp. end) node of the list and if @p begin (resp. @p end) + * If one of the nodes to remove is the beginning (resp. end) node of the list, and if @p begin (resp. @p end) * addresses are provided, the pointers are adjusted accordingly. * * The following combinations of arguments are valid (more arguments are optional): @@ -405,20 +391,13 @@ void cx_linked_list_insert_sorted_chain( * @return the actual number of nodes that were removed (can be less when the list did not have enough nodes) */ cx_attr_nonnull_arg(5) -cx_attr_export -size_t cx_linked_list_remove_chain( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *node, - size_t num -); +CX_EXPORT size_t cx_linked_list_remove_chain(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, size_t num); /** * Removes a node from the linked list. * - * If the node to remove is the beginning (resp. end) node of the list and if @p begin (resp. @p end) + * If the node to remove is the beginning (resp. end) node of the list, and if @p begin (resp. @p end) * addresses are provided, the pointers are adjusted accordingly. * * The following combinations of arguments are valid (more arguments are optional): @@ -435,15 +414,8 @@ size_t cx_linked_list_remove_chain( * @param node the node to remove */ cx_attr_nonnull_arg(5) -static inline void cx_linked_list_remove( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *node -) { - cx_linked_list_remove_chain(begin, end, loc_prev, loc_next, node, 1); -} +CX_EXPORT void cx_linked_list_remove(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node); /** * Determines the size of a linked list starting with @p node. @@ -453,11 +425,7 @@ static inline void cx_linked_list_remove( * @return the size of the list or zero if @p node is @c NULL */ cx_attr_nodiscard -cx_attr_export -size_t cx_linked_list_size( - const void *node, - ptrdiff_t loc_next -); +CX_EXPORT size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next); /** * Sorts a linked list based on a comparison function. @@ -482,21 +450,14 @@ size_t cx_linked_list_size( * @param cmp_func the compare function defining the sort order */ cx_attr_nonnull_arg(1, 6) -cx_attr_export -void cx_linked_list_sort( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - ptrdiff_t loc_data, - cx_compare_func cmp_func -); +CX_EXPORT void cx_linked_list_sort(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func cmp_func); /** * Compares two lists element wise. * - * @attention Both list must have the same structure. + * @attention Both lists must have the same structure. * * @param begin_left the beginning of the left list (@c NULL denotes an empty list) * @param begin_right the beginning of the right list (@c NULL denotes an empty list) @@ -507,14 +468,8 @@ void cx_linked_list_sort( * right list, positive if the left list is larger than the right list, zero if both lists are equal. */ cx_attr_nonnull_arg(5) -cx_attr_export -int cx_linked_list_compare( - const void *begin_left, - const void *begin_right, - ptrdiff_t loc_advance, - ptrdiff_t loc_data, - cx_compare_func cmp_func -); +CX_EXPORT int cx_linked_list_compare(const void *begin_left, const void *begin_right, + ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func); /** * Reverses the order of the nodes in a linked list. @@ -525,13 +480,7 @@ int cx_linked_list_compare( * @param loc_next the location of a @c next pointer within your node struct (required) */ cx_attr_nonnull_arg(1) -cx_attr_export -void cx_linked_list_reverse( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next); #ifdef __cplusplus } // extern "C" diff --git a/ucx/cx/list.h b/ucx/cx/list.h index 14ddc04..4722261 100644 --- a/ucx/cx/list.h +++ b/ucx/cx/list.h @@ -80,46 +80,41 @@ struct cx_list_class_s { /** * Member function for inserting a single element. - * The data pointer may be @c NULL in which case the function shall only allocate memory. - * Returns a pointer to the data of the inserted element. + * The data pointer may be @c NULL, in which case the function shall only allocate memory. + * Returns a pointer to the allocated memory or @c NULL if allocation fails. */ - void *(*insert_element)( - struct cx_list_s *list, - size_t index, - const void *data - ); + void *(*insert_element)(struct cx_list_s *list, size_t index, const void *data); /** * Member function for inserting multiple elements. * + * The data pointer may be @c NULL, in which case the function shall only allocate memory. + * Returns the number of successfully inserted or allocated elements. + * * @see cx_list_default_insert_array() */ - size_t (*insert_array)( - struct cx_list_s *list, - size_t index, - const void *data, - size_t n - ); + size_t (*insert_array)(struct cx_list_s *list, size_t index, const void *data, size_t n); /** * Member function for inserting sorted elements into a sorted list. + * Returns the number of successfully inserted elements. * * @see cx_list_default_insert_sorted() */ - size_t (*insert_sorted)( - struct cx_list_s *list, - const void *sorted_data, - size_t n - ); + size_t (*insert_sorted)(struct cx_list_s *list, const void *sorted_data, size_t n); + + /** + * Member function for inserting multiple elements if they do not exist. + * Implementations shall return the number of successfully processed elements + * (including those which were not added because they are already contained). + * @see cx_list_default_insert_unique() + */ + size_t (*insert_unique)(struct cx_list_s *list, const void *sorted_data, size_t n); /** * Member function for inserting an element relative to an iterator position. */ - int (*insert_iter)( - struct cx_iterator_s *iter, - const void *elem, - int prepend - ); + int (*insert_iter)(struct cx_iterator_s *iter, const void *elem, int prepend); /** * Member function for removing elements. @@ -131,12 +126,7 @@ struct cx_list_class_s { * The function SHALL return the actual number of elements removed, which * might be lower than @p num when going out of bounds. */ - size_t (*remove)( - struct cx_list_s *list, - size_t index, - size_t num, - void *targetbuf - ); + size_t (*remove)(struct cx_list_s *list, size_t index, size_t num, void *targetbuf); /** * Member function for removing all elements. @@ -148,28 +138,17 @@ struct cx_list_class_s { * * @see cx_list_default_swap() */ - int (*swap)( - struct cx_list_s *list, - size_t i, - size_t j - ); + int (*swap)(struct cx_list_s *list, size_t i, size_t j); /** * Member function for element lookup. */ - void *(*at)( - const struct cx_list_s *list, - size_t index - ); + void *(*at)(const struct cx_list_s *list, size_t index); /** * Member function for finding and optionally removing an element. */ - size_t (*find_remove)( - struct cx_list_s *list, - const void *elem, - bool remove - ); + size_t (*find_remove)(struct cx_list_s *list, const void *elem, bool remove); /** * Member function for sorting the list. @@ -181,13 +160,9 @@ struct cx_list_class_s { /** * Optional member function for comparing this list * to another list of the same type. - * If set to @c NULL, comparison won't be optimized. + * If set to @c NULL, the comparison won't be optimized. */ - cx_attr_nonnull - int (*compare)( - const struct cx_list_s *list, - const struct cx_list_s *other - ); + int (*compare)(const struct cx_list_s *list, const struct cx_list_s *other); /** * Member function for reversing the order of the items. @@ -197,11 +172,7 @@ struct cx_list_class_s { /** * Member function for returning an iterator pointing to the specified index. */ - struct cx_iterator_s (*iterator)( - const struct cx_list_s *list, - size_t index, - bool backward - ); + struct cx_iterator_s (*iterator)(const struct cx_list_s *list, size_t index, bool backward); }; /** @@ -214,11 +185,10 @@ typedef struct cx_list_s CxList; * * Writing to that list is not allowed. * - * You can use this is a placeholder for initializing CxList pointers + * You can use this as a placeholder for initializing CxList pointers * for which you do not want to reserve memory right from the beginning. */ -cx_attr_export -extern CxList *const cxEmptyList; +CX_EXPORT extern CxList *const cxEmptyList; /** * Default implementation of an array insert. @@ -235,13 +205,8 @@ extern CxList *const cxEmptyList; * @return the number of elements actually inserted */ cx_attr_nonnull -cx_attr_export -size_t cx_list_default_insert_array( - struct cx_list_s *list, - size_t index, - const void *data, - size_t n -); +CX_EXPORT size_t cx_list_default_insert_array(struct cx_list_s *list, + size_t index, const void *data, size_t n); /** * Default implementation of a sorted insert. @@ -260,12 +225,28 @@ size_t cx_list_default_insert_array( * @return the number of elements actually inserted */ cx_attr_nonnull -cx_attr_export -size_t cx_list_default_insert_sorted( - struct cx_list_s *list, - const void *sorted_data, - size_t n -); +CX_EXPORT size_t cx_list_default_insert_sorted(struct cx_list_s *list, + const void *sorted_data, size_t n); + +/** + * Default implementation of an array insert where only elements are inserted when they don't exist in the list. + * + * This function is similar to cx_list_default_insert_sorted(), except it skips elements that are already in the list. + * + * @note The return value of this function denotes the number of elements from the @p sorted_data that are definitely + * contained in the list after completing the call. It is @em not the number of elements that were newly inserted. + * That means, when no error occurred, the return value should be @p n. + * + * Use this in your own list class if you do not want to implement an optimized version for your list. + * + * @param list the list + * @param sorted_data a pointer to the array of pre-sorted data to insert + * @param n the number of elements to insert + * @return the number of elements from the @p sorted_data that are definitely present in the list after this call + */ +cx_attr_nonnull +CX_EXPORT size_t cx_list_default_insert_unique(struct cx_list_s *list, + const void *sorted_data, size_t n); /** * Default unoptimized sort implementation. @@ -279,8 +260,7 @@ size_t cx_list_default_insert_sorted( * @param list the list that shall be sorted */ cx_attr_nonnull -cx_attr_export -void cx_list_default_sort(struct cx_list_s *list); +CX_EXPORT void cx_list_default_sort(struct cx_list_s *list); /** * Default unoptimized swap implementation. @@ -296,20 +276,19 @@ void cx_list_default_sort(struct cx_list_s *list); * allocation for the temporary buffer fails */ cx_attr_nonnull -cx_attr_export -int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j); +CX_EXPORT int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j); /** * Initializes a list struct. * * Only use this function if you are creating your own list implementation. * The purpose of this function is to be called in the initialization code - * of your list, to set certain members correctly. + * of your list to set certain members correctly. * * This is particularly important when you want your list to support * #CX_STORE_POINTERS as @p elem_size. This function will wrap the list * class accordingly and make sure that you can implement your list as if - * it was only storing objects and the wrapper will automatically enable + * it was only storing objects, and the wrapper will automatically enable * the feature of storing pointers. * * @par Example @@ -344,14 +323,9 @@ int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j); * @param elem_size the size of one element */ cx_attr_nonnull_arg(1, 2, 3) -cx_attr_export -void cx_list_init( - struct cx_list_s *list, - struct cx_list_class_s *cl, - const struct cx_allocator_s *allocator, - cx_compare_func comparator, - size_t elem_size -); +CX_EXPORT void cx_list_init(struct cx_list_s *list, + struct cx_list_class_s *cl, const struct cx_allocator_s *allocator, + cx_compare_func comparator, size_t elem_size); /** * Returns the number of elements currently stored in the list. @@ -360,9 +334,7 @@ void cx_list_init( * @return the number of currently stored elements */ cx_attr_nonnull -static inline size_t cxListSize(const CxList *list) { - return list->collection.size; -} +CX_EXPORT size_t cxListSize(const CxList *list); /** * Adds an item to the end of the list. @@ -375,13 +347,7 @@ static inline size_t cxListSize(const CxList *list) { * @see cxListEmplace() */ cx_attr_nonnull -static inline int cxListAdd( - CxList *list, - const void *elem -) { - list->collection.sorted = false; - return list->cl->insert_element(list, list->collection.size, elem) == NULL; -} +CX_EXPORT int cxListAdd(CxList *list, const void *elem); /** * Adds multiple items to the end of the list. @@ -391,28 +357,22 @@ static inline int cxListAdd( * If there is not enough memory to add all elements, the returned value is * less than @p n. * - * If this list is storing pointers instead of objects @p array is expected to + * If this list is storing pointers instead of objects, @p array is expected to * be an array of pointers. * * @param list the list * @param array a pointer to the elements to add * @param n the number of elements to add * @return the number of added elements + * @see cxListEmplaceArray() */ cx_attr_nonnull -static inline size_t cxListAddArray( - CxList *list, - const void *array, - size_t n -) { - list->collection.sorted = false; - return list->cl->insert_array(list, list->collection.size, array, n); -} +CX_EXPORT size_t cxListAddArray(CxList *list, const void *array, size_t n); /** * Inserts an item at the specified index. * - * If @p index equals the list @c size, this is effectively cxListAdd(). + * If the @p index equals the list @c size, this is effectively cxListAdd(). * * @param list the list * @param index the index the element shall have @@ -424,14 +384,7 @@ static inline size_t cxListAddArray( * @see cxListEmplaceAt() */ cx_attr_nonnull -static inline int cxListInsert( - CxList *list, - size_t index, - const void *elem -) { - list->collection.sorted = false; - return list->cl->insert_element(list, index, elem) == NULL; -} +CX_EXPORT int cxListInsert(CxList *list, size_t index, const void *elem); /** * Allocates memory for an element at the specified index and returns a pointer to that memory. @@ -442,14 +395,11 @@ static inline int cxListInsert( * @param index the index where to emplace the element * @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds * @see cxListEmplace() + * @see cxListEmplaceArrayAt() * @see cxListInsert() */ cx_attr_nonnull -static inline void *cxListEmplaceAt(CxList *list, size_t index) { - list->collection.sorted = false; - return list->cl->insert_element(list, index, NULL); -} - +CX_EXPORT void *cxListEmplaceAt(CxList *list, size_t index); /** * Allocates memory for an element at the end of the list and returns a pointer to that memory. @@ -462,10 +412,46 @@ static inline void *cxListEmplaceAt(CxList *list, size_t index) { * @see cxListAdd() */ cx_attr_nonnull -static inline void *cxListEmplace(CxList *list) { - list->collection.sorted = false; - return list->cl->insert_element(list, list->collection.size, NULL); -} +CX_EXPORT void *cxListEmplace(CxList *list); + +/** + * Allocates memory for multiple elements and returns an iterator. + * + * The iterator will only iterate over the successfully allocated elements. + * The @c elem_count attribute is set to that number, and the @c index attribute + * will range from zero to @c elem_count minus one. + * + * @remark When the list is storing pointers, the iterator will iterate over + * the @c void** elements. + * + * @param list the list + * @param index the index where to insert the new data + * @param n the number of elements for which to allocate the memory + * @return an iterator, iterating over the new memory + * @see cxListEmplaceAt() + * @see cxListInsertArray() + */ +cx_attr_nonnull +CX_EXPORT CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n); + +/** + * Allocates memory for multiple elements and returns an iterator. + * + * The iterator will only iterate over the successfully allocated elements. + * The @c elem_count attribute is set to that number, and the @c index attribute + * will range from zero to @c elem_count minus one. + * + * @remark When the list is storing pointers, the iterator will iterate over + * the @c void** elements. + * + * @param list the list + * @param n the number of elements for which to allocate the memory + * @return an iterator, iterating over the new memory + * @see cxListEmplace() + * @see cxListAddArray() + */ +cx_attr_nonnull +CX_EXPORT CxIterator cxListEmplaceArray(CxList *list, size_t n); /** * Inserts an item into a sorted list. @@ -478,18 +464,27 @@ static inline void *cxListEmplace(CxList *list) { * @retval non-zero memory allocation failure */ cx_attr_nonnull -static inline int cxListInsertSorted( - CxList *list, - const void *elem -) { - list->collection.sorted = true; // guaranteed by definition - const void *data = list->collection.store_pointer ? &elem : elem; - return list->cl->insert_sorted(list, data, 1) == 0; -} +CX_EXPORT int cxListInsertSorted(CxList *list, const void *elem); + +/** + * Inserts an item into a list if it does not exist. + * + * If the list is not sorted already, this function will check all elements + * and append the new element when it was not found. + * It is strongly recommended to use this function only on sorted lists, where + * the element, if it is not contained, is inserted at the correct position. + * + * @param list the list + * @param elem a pointer to the element to add + * @retval zero success (also when the element was already in the list) + * @retval non-zero memory allocation failure + */ +cx_attr_nonnull +CX_EXPORT int cxListInsertUnique(CxList *list, const void *elem); /** * Inserts multiple items to the list at the specified index. - * If @p index equals the list size, this is effectively cxListAddArray(). + * If the @p index equals the list size, this is effectively cxListAddArray(). * * This method is usually more efficient than invoking cxListInsert() * multiple times. @@ -497,7 +492,7 @@ static inline int cxListInsertSorted( * If there is not enough memory to add all elements, the returned value is * less than @p n. * - * If this list is storing pointers instead of objects @p array is expected to + * If this list is storing pointers instead of objects, @p array is expected to * be an array of pointers. * * @param list the list @@ -505,28 +500,21 @@ static inline int cxListInsertSorted( * @param array a pointer to the elements to add * @param n the number of elements to add * @return the number of added elements + * @see cxListEmplaceArrayAt() */ cx_attr_nonnull -static inline size_t cxListInsertArray( - CxList *list, - size_t index, - const void *array, - size_t n -) { - list->collection.sorted = false; - return list->cl->insert_array(list, index, array, n); -} +CX_EXPORT size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n); /** * Inserts a sorted array into a sorted list. * - * This method is usually more efficient than inserting each element separately, + * This method is usually more efficient than inserting each element separately * because consecutive chunks of sorted data are inserted in one pass. * * If there is not enough memory to add all elements, the returned value is * less than @p n. * - * If this list is storing pointers instead of objects @p array is expected to + * If this list is storing pointers instead of objects, @p array is expected to * be an array of pointers. * * If the list is not sorted already, the behavior is undefined. @@ -537,14 +525,42 @@ static inline size_t cxListInsertArray( * @return the number of added elements */ cx_attr_nonnull -static inline size_t cxListInsertSortedArray( - CxList *list, - const void *array, - size_t n -) { - list->collection.sorted = true; // guaranteed by definition - return list->cl->insert_sorted(list, array, n); -} +CX_EXPORT size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n); + +/** + * Inserts an array into a list, skipping duplicates. + * + * The @p list does not need to be sorted (in contrast to cxListInsertSortedArray()). + * But it is strongly recommended to use this function only on sorted lists, + * because otherwise it will fall back to an inefficient algorithm which inserts + * all elements one by one. + * If the @p list is not sorted, the @p array also does not need to be sorted. + * But when the @p list is sorted, the @p array must also be sorted. + * + * This method is usually more efficient than inserting each element separately + * because consecutive chunks of sorted data are inserted in one pass. + * + * If there is not enough memory to add all elements, the returned value is + * less than @p n. + * + * @note The return value of this function denotes the number of elements + * from the @p sorted_data that are definitely contained in the list after + * completing the call. It is @em not the number of elements that were newly + * inserted. That means, when no error occurred, the return value should + * be @p n. + * + * If this list is storing pointers instead of objects @p array is expected to + * be an array of pointers. + * + * @param list the list + * @param array a pointer to the elements to add + * @param n the number of elements to add + * @return the number of added elements + * + * @return the number of elements from the @p sorted_data that are definitely present in the list after this call + */ +cx_attr_nonnull +CX_EXPORT size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n); /** * Inserts an element after the current location of the specified iterator. @@ -563,14 +579,7 @@ static inline size_t cxListInsertSortedArray( * @see cxListInsertBefore() */ cx_attr_nonnull -static inline int cxListInsertAfter( - CxIterator *iter, - const void *elem -) { - CxList* list = (CxList*)iter->src_handle.m; - list->collection.sorted = false; - return list->cl->insert_iter(iter, elem, 0); -} +CX_EXPORT int cxListInsertAfter(CxIterator *iter, const void *elem); /** * Inserts an element before the current location of the specified iterator. @@ -589,14 +598,7 @@ static inline int cxListInsertAfter( * @see cxListInsertAfter() */ cx_attr_nonnull -static inline int cxListInsertBefore( - CxIterator *iter, - const void *elem -) { - CxList* list = (CxList*)iter->src_handle.m; - list->collection.sorted = false; - return list->cl->insert_iter(iter, elem, 1); -} +CX_EXPORT int cxListInsertBefore(CxIterator *iter, const void *elem); /** * Removes the element at the specified index. @@ -610,12 +612,7 @@ static inline int cxListInsertBefore( * @retval non-zero index out of bounds */ cx_attr_nonnull -static inline int cxListRemove( - CxList *list, - size_t index -) { - return list->cl->remove(list, index, 1, NULL) == 0; -} +CX_EXPORT int cxListRemove(CxList *list, size_t index); /** * Removes and returns the element at the specified index. @@ -630,15 +627,8 @@ static inline int cxListRemove( * @retval zero success * @retval non-zero index out of bounds */ -cx_attr_nonnull -cx_attr_access_w(3) -static inline int cxListRemoveAndGet( - CxList *list, - size_t index, - void *targetbuf -) { - return list->cl->remove(list, index, 1, targetbuf) == 0; -} +cx_attr_nonnull cx_attr_access_w(3) +CX_EXPORT int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf); /** * Removes and returns the first element of the list. @@ -650,18 +640,12 @@ static inline int cxListRemoveAndGet( * @param list the list * @param targetbuf a buffer where to copy the element * @retval zero success - * @retval non-zero list is empty + * @retval non-zero the list is empty * @see cxListPopFront() * @see cxListRemoveAndGetLast() */ -cx_attr_nonnull -cx_attr_access_w(2) -static inline int cxListRemoveAndGetFirst( - CxList *list, - void *targetbuf -) { - return list->cl->remove(list, 0, 1, targetbuf) == 0; -} +cx_attr_nonnull cx_attr_access_w(2) +CX_EXPORT int cxListRemoveAndGetFirst(CxList *list, void *targetbuf); /** * Removes and returns the first element of the list. @@ -675,7 +659,7 @@ static inline int cxListRemoveAndGetFirst( * @param list (@c CxList*) the list * @param targetbuf (@c void*) a buffer where to copy the element * @retval zero success - * @retval non-zero list is empty + * @retval non-zero the list is empty * @see cxListRemoveAndGetFirst() * @see cxListPop() */ @@ -692,17 +676,10 @@ static inline int cxListRemoveAndGetFirst( * @param list the list * @param targetbuf a buffer where to copy the element * @retval zero success - * @retval non-zero list is empty + * @retval non-zero the list is empty */ -cx_attr_nonnull -cx_attr_access_w(2) -static inline int cxListRemoveAndGetLast( - CxList *list, - void *targetbuf -) { - // note: index may wrap - member function will catch that - return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0; -} +cx_attr_nonnull cx_attr_access_w(2) +CX_EXPORT int cxListRemoveAndGetLast(CxList *list, void *targetbuf); /** * Removes and returns the last element of the list. @@ -716,7 +693,7 @@ static inline int cxListRemoveAndGetLast( * @param list (@c CxList*) the list * @param targetbuf (@c void*) a buffer where to copy the element * @retval zero success - * @retval non-zero list is empty + * @retval non-zero the list is empty * @see cxListRemoveAndGetLast() * @see cxListPopFront() */ @@ -738,13 +715,7 @@ static inline int cxListRemoveAndGetLast( * @return the actual number of removed elements */ cx_attr_nonnull -static inline size_t cxListRemoveArray( - CxList *list, - size_t index, - size_t num -) { - return list->cl->remove(list, index, num, NULL); -} +CX_EXPORT size_t cxListRemoveArray(CxList *list, size_t index, size_t num); /** * Removes and returns multiple elements starting at the specified index. @@ -759,16 +730,8 @@ static inline size_t cxListRemoveArray( * @param targetbuf a buffer where to copy the elements * @return the actual number of removed elements */ -cx_attr_nonnull -cx_attr_access_w(4) -static inline size_t cxListRemoveArrayAndGet( - CxList *list, - size_t index, - size_t num, - void *targetbuf -) { - return list->cl->remove(list, index, num, targetbuf); -} +cx_attr_nonnull cx_attr_access_w(4) +CX_EXPORT size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf); /** * Removes all elements from this list. @@ -779,10 +742,7 @@ static inline size_t cxListRemoveArrayAndGet( * @param list the list */ cx_attr_nonnull -static inline void cxListClear(CxList *list) { - list->collection.sorted = true; // empty lists are always sorted - list->cl->clear(list); -} +CX_EXPORT void cxListClear(CxList *list); /** * Swaps two items in the list. @@ -798,14 +758,7 @@ static inline void cxListClear(CxList *list) { * or the swap needed extra memory, but allocation failed */ cx_attr_nonnull -static inline int cxListSwap( - CxList *list, - size_t i, - size_t j -) { - list->collection.sorted = false; - return list->cl->swap(list, i, j); -} +CX_EXPORT int cxListSwap(CxList *list, size_t i, size_t j); /** * Returns a pointer to the element at the specified index. @@ -817,12 +770,7 @@ static inline int cxListSwap( * @return a pointer to the element or @c NULL if the index is out of bounds */ cx_attr_nonnull -static inline void *cxListAt( - const CxList *list, - size_t index -) { - return list->cl->at(list, index); -} +CX_EXPORT void *cxListAt(const CxList *list, size_t index); /** * Returns a pointer to the first element. @@ -833,9 +781,7 @@ static inline void *cxListAt( * @return a pointer to the first element or @c NULL if the list is empty */ cx_attr_nonnull -static inline void *cxListFirst(const CxList *list) { - return list->cl->at(list, 0); -} +CX_EXPORT void *cxListFirst(const CxList *list); /** * Returns a pointer to the last element. @@ -846,12 +792,13 @@ static inline void *cxListFirst(const CxList *list) { * @return a pointer to the last element or @c NULL if the list is empty */ cx_attr_nonnull -static inline void *cxListLast(const CxList *list) { - return list->cl->at(list, list->collection.size - 1); -} +CX_EXPORT void *cxListLast(const CxList *list); /** - * Sets the element at the specified index in the list + * Sets the element at the specified index in the list. + * + * This overwrites the element in-place without calling any destructor + * on the overwritten element. * * @param list the list to set the element in * @param index the index to set the element at @@ -860,91 +807,35 @@ static inline void *cxListLast(const CxList *list) { * @retval non-zero when index is out of bounds */ cx_attr_nonnull -cx_attr_export -int cxListSet( - CxList *list, - size_t index, - const void *elem -); +CX_EXPORT int cxListSet(CxList *list, size_t index, const void *elem); /** * Returns an iterator pointing to the item at the specified index. * * The returned iterator is position-aware. * - * If the index is out of range, a past-the-end iterator will be returned. + * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned. * * @param list the list * @param index the index where the iterator shall point at * @return a new iterator */ -cx_attr_nonnull cx_attr_nodiscard -static inline CxIterator cxListIteratorAt( - const CxList *list, - size_t index -) { - return list->cl->iterator(list, index, false); -} +CX_EXPORT CxIterator cxListIteratorAt(const CxList *list, size_t index); /** * Returns a backwards iterator pointing to the item at the specified index. * * The returned iterator is position-aware. * - * If the index is out of range, a past-the-end iterator will be returned. - * - * @param list the list - * @param index the index where the iterator shall point at - * @return a new iterator - */ -cx_attr_nonnull -cx_attr_nodiscard -static inline CxIterator cxListBackwardsIteratorAt( - const CxList *list, - size_t index -) { - return list->cl->iterator(list, index, true); -} - -/** - * Returns a mutating iterator pointing to the item at the specified index. - * - * The returned iterator is position-aware. - * - * If the index is out of range, a past-the-end iterator will be returned. - * - * @param list the list - * @param index the index where the iterator shall point at - * @return a new iterator - */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxIterator cxListMutIteratorAt( - CxList *list, - size_t index -); - -/** - * Returns a mutating backwards iterator pointing to the item at the - * specified index. - * - * The returned iterator is position-aware. - * - * If the index is out of range, a past-the-end iterator will be returned. + * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned. * * @param list the list * @param index the index where the iterator shall point at * @return a new iterator */ -cx_attr_nonnull cx_attr_nodiscard -cx_attr_export -CxIterator cxListMutBackwardsIteratorAt( - CxList *list, - size_t index -); +CX_EXPORT CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index); /** * Returns an iterator pointing to the first item of the list. @@ -957,27 +848,7 @@ CxIterator cxListMutBackwardsIteratorAt( * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListIterator(const CxList *list) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, 0, false); -} - -/** - * Returns a mutating iterator pointing to the first item of the list. - * - * The returned iterator is position-aware. - * - * If the list is empty or @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @return a new iterator - */ -cx_attr_nodiscard -static inline CxIterator cxListMutIterator(CxList *list) { - if (list == NULL) list = cxEmptyList; - return cxListMutIteratorAt(list, 0); -} - +CX_EXPORT CxIterator cxListIterator(const CxList *list); /** * Returns a backwards iterator pointing to the last item of the list. @@ -990,26 +861,7 @@ static inline CxIterator cxListMutIterator(CxList *list) { * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListBackwardsIterator(const CxList *list) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, list->collection.size - 1, true); -} - -/** - * Returns a mutating backwards iterator pointing to the last item of the list. - * - * The returned iterator is position-aware. - * - * If the list is empty or @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @return a new iterator - */ -cx_attr_nodiscard -static inline CxIterator cxListMutBackwardsIterator(CxList *list) { - if (list == NULL) list = cxEmptyList; - return cxListMutBackwardsIteratorAt(list, list->collection.size - 1); -} +CX_EXPORT CxIterator cxListBackwardsIterator(const CxList *list); /** * Returns the index of the first element that equals @p elem. @@ -1022,17 +874,11 @@ static inline CxIterator cxListMutBackwardsIterator(CxList *list) { * @see cxListIndexValid() * @see cxListContains() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline size_t cxListFind( - const CxList *list, - const void *elem -) { - return list->cl->find_remove((CxList*)list, elem, false); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxListFind(const CxList *list, const void *elem); /** - * Checks, if the list contains the specified element. + * Checks if the list contains the specified element. * * The elements are compared with the list's comparator function. * @@ -1042,14 +888,8 @@ static inline size_t cxListFind( * @retval false if the element is not contained * @see cxListFind() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline bool cxListContains( - const CxList* list, - const void* elem -) { - return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size; -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT bool cxListContains(const CxList* list, const void* elem); /** * Checks if the specified index is within bounds. @@ -1059,11 +899,8 @@ static inline bool cxListContains( * @retval true if the index is within bounds * @retval false if the index is out of bounds */ -cx_attr_nonnull -cx_attr_nodiscard -static inline bool cxListIndexValid(const CxList *list, size_t index) { - return index < list->collection.size; -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT bool cxListIndexValid(const CxList *list, size_t index); /** * Removes and returns the index of the first element that equals @p elem. @@ -1077,12 +914,7 @@ static inline bool cxListIndexValid(const CxList *list, size_t index) { * @see cxListIndexValid() */ cx_attr_nonnull -static inline size_t cxListFindRemove( - CxList *list, - const void *elem -) { - return list->cl->find_remove(list, elem, true); -} +CX_EXPORT size_t cxListFindRemove(CxList *list, const void *elem); /** * Sorts the list. @@ -1092,11 +924,7 @@ static inline size_t cxListFindRemove( * @param list the list */ cx_attr_nonnull -static inline void cxListSort(CxList *list) { - if (list->collection.sorted) return; - list->cl->sort(list); - list->collection.sorted = true; -} +CX_EXPORT void cxListSort(CxList *list); /** * Reverses the order of the items. @@ -1104,11 +932,7 @@ static inline void cxListSort(CxList *list) { * @param list the list */ cx_attr_nonnull -static inline void cxListReverse(CxList *list) { - // still sorted, but not according to the cmp_func - list->collection.sorted = false; - list->cl->reverse(list); -} +CX_EXPORT void cxListReverse(CxList *list); /** * Compares a list to another list of the same type. @@ -1119,28 +943,22 @@ static inline void cxListReverse(CxList *list) { * @param list the list * @param other the list to compare to * @retval zero both lists are equal element wise - * @retval negative the first list is smaller + * @retval negative the first list is smaller, * or the first non-equal element in the first list is smaller * @retval positive the first list is larger * or the first non-equal element in the first list is larger */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cxListCompare( - const CxList *list, - const CxList *other -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cxListCompare(const CxList *list, const CxList *other); /** * Deallocates the memory of the specified list structure. * * Also calls the content destructor functions for each element, if specified. * - * @param list the list which shall be freed + * @param list the list that shall be freed */ -cx_attr_export -void cxListFree(CxList *list); +CX_EXPORT void cxListFree(CxList *list); #ifdef __cplusplus diff --git a/ucx/cx/map.h b/ucx/cx/map.h index 36067d5..0b3060d 100644 --- a/ucx/cx/map.h +++ b/ucx/cx/map.h @@ -111,16 +111,7 @@ struct cx_map_iterator_s { /** * Handle for the source map. */ - union { - /** - * Access for mutating iterators. - */ - CxMap *m; - /** - * Access for normal iterators. - */ - const CxMap *c; - } map; + CxMap *map; /** * Handle for the current element. @@ -185,20 +176,16 @@ struct cx_map_class_s { /** * Add or overwrite an element. + * If the @p value is @c NULL, the implementation + * shall only allocate memory instead of adding an existing value to the map. + * Returns a pointer to the allocated memory or @c NULL if allocation fails. */ - int (*put)( - CxMap *map, - CxHashKey key, - void *value - ); + void *(*put)(CxMap *map, CxHashKey key, void *value); /** * Returns an element. */ - void *(*get)( - const CxMap *map, - CxHashKey key - ); + void *(*get)(const CxMap *map, CxHashKey key); /** * Removes an element. @@ -210,11 +197,7 @@ struct cx_map_class_s { * The function SHALL return zero when the @p key was found and * non-zero, otherwise. */ - int (*remove)( - CxMap *map, - CxHashKey key, - void *targetbuf - ); + int (*remove)(CxMap *map, CxHashKey key, void *targetbuf); /** * Creates an iterator for this map. @@ -227,11 +210,10 @@ struct cx_map_class_s { * * Writing to that map is not allowed. * - * You can use this is a placeholder for initializing CxMap pointers + * You can use this as a placeholder for initializing CxMap pointers * for which you do not want to reserve memory right from the beginning. */ -cx_attr_export -extern CxMap *const cxEmptyMap; +CX_EXPORT extern CxMap *const cxEmptyMap; /** * Deallocates the memory of the specified map. @@ -240,8 +222,7 @@ extern CxMap *const cxEmptyMap; * * @param map the map to be freed */ -cx_attr_export -void cxMapFree(CxMap *map); +CX_EXPORT void cxMapFree(CxMap *map); /** @@ -252,9 +233,7 @@ void cxMapFree(CxMap *map); * @param map the map to be cleared */ cx_attr_nonnull -static inline void cxMapClear(CxMap *map) { - map->cl->clear(map); -} +CX_EXPORT void cxMapClear(CxMap *map); /** * Returns the number of elements in this map. @@ -263,9 +242,7 @@ static inline void cxMapClear(CxMap *map) { * @return the number of stored elements */ cx_attr_nonnull -static inline size_t cxMapSize(const CxMap *map) { - return map->collection.size; -} +CX_EXPORT size_t cxMapSize(const CxMap *map); /** * Creates a value iterator for a map. @@ -277,308 +254,66 @@ static inline size_t cxMapSize(const CxMap *map) { * @note An iterator iterates over all elements successively. Therefore, the order * highly depends on the map implementation and may change arbitrarily when the contents change. * - * @param map the map to create the iterator for + * @param map the map to create the iterator for (can be @c NULL) * @return an iterator for the currently stored values */ -cx_attr_nonnull cx_attr_nodiscard -static inline CxMapIterator cxMapIteratorValues(const CxMap *map) { - return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); -} +CX_EXPORT CxMapIterator cxMapIteratorValues(const CxMap *map); /** * Creates a key iterator for a map. * - * The elements of the iterator are keys of type CxHashKey and the pointer returned + * The elements of the iterator are keys of type CxHashKey, and the pointer returned * during iterator shall be treated as @c const @c CxHashKey* . * * @note An iterator iterates over all elements successively. Therefore, the order * highly depends on the map implementation and may change arbitrarily when the contents change. * - * @param map the map to create the iterator for + * @param map the map to create the iterator for (can be @c NULL) * @return an iterator for the currently stored keys */ -cx_attr_nonnull cx_attr_nodiscard -static inline CxMapIterator cxMapIteratorKeys(const CxMap *map) { - return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); -} +CX_EXPORT CxMapIterator cxMapIteratorKeys(const CxMap *map); /** * Creates an iterator for a map. * - * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned + * The elements of the iterator are key/value pairs of type CxMapEntry, and the pointer returned * during iterator shall be treated as @c const @c CxMapEntry* . * * @note An iterator iterates over all elements successively. Therefore, the order * highly depends on the map implementation and may change arbitrarily when the contents change. * - * @param map the map to create the iterator for + * @param map the map to create the iterator for (can be @c NULL) * @return an iterator for the currently stored entries * @see cxMapIteratorKeys() * @see cxMapIteratorValues() */ -cx_attr_nonnull cx_attr_nodiscard -static inline CxMapIterator cxMapIterator(const CxMap *map) { - return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); -} - +CX_EXPORT CxMapIterator cxMapIterator(const CxMap *map); /** - * Creates a mutating iterator over the values of a map. - * - * When the map is storing pointers, those pointers are returned. - * Otherwise, the iterator iterates over pointers to the memory within the map where the - * respective elements are stored. - * - * @note An iterator iterates over all elements successively. Therefore, the order - * highly depends on the map implementation and may change arbitrarily when the contents change. - * - * @param map the map to create the iterator for - * @return an iterator for the currently stored values - */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxMapIterator cxMapMutIteratorValues(CxMap *map); - -/** - * Creates a mutating iterator over the keys of a map. - * - * The elements of the iterator are keys of type CxHashKey and the pointer returned - * during iterator shall be treated as @c const @c CxHashKey* . - * - * @note An iterator iterates over all elements successively. Therefore, the order - * highly depends on the map implementation and may change arbitrarily when the contents change. + * Puts a key/value-pair into the map. * - * @param map the map to create the iterator for - * @return an iterator for the currently stored keys - */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxMapIterator cxMapMutIteratorKeys(CxMap *map); - -/** - * Creates a mutating iterator for a map. + * A possible existing value will be overwritten. + * If destructor functions are specified, they are called for + * the overwritten element. * - * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned - * during iterator shall be treated as @c const @c CxMapEntry* . + * If this map is storing pointers, the @p value pointer is written + * to the map. Otherwise, the memory is copied from @p value with + * memcpy(). * - * @note An iterator iterates over all elements successively. Therefore, the order - * highly depends on the map implementation and may change arbitrarily when the contents change. + * The @p key is always copied. * - * @param map the map to create the iterator for - * @return an iterator for the currently stored entries - * @see cxMapMutIteratorKeys() - * @see cxMapMutIteratorValues() - */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxMapIterator cxMapMutIterator(CxMap *map); - -#ifdef __cplusplus -} // end the extern "C" block here, because we want to start overloading -cx_attr_nonnull -static inline int cxMapPut( - CxMap *map, - CxHashKey const &key, - void *value -) { - return map->cl->put(map, key, value); -} - -cx_attr_nonnull -static inline int cxMapPut( - CxMap *map, - cxstring const &key, - void *value -) { - return map->cl->put(map, cx_hash_key_cxstr(key), value); -} - -cx_attr_nonnull -static inline int cxMapPut( - CxMap *map, - cxmutstr const &key, - void *value -) { - return map->cl->put(map, cx_hash_key_cxstr(key), value); -} - -cx_attr_nonnull -cx_attr_cstr_arg(2) -static inline int cxMapPut( - CxMap *map, - const char *key, - void *value -) { - return map->cl->put(map, cx_hash_key_str(key), value); -} - -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cxMapGet( - const CxMap *map, - CxHashKey const &key -) { - return map->cl->get(map, key); -} - -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cxMapGet( - const CxMap *map, - cxstring const &key -) { - return map->cl->get(map, cx_hash_key_cxstr(key)); -} - -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cxMapGet( - const CxMap *map, - cxmutstr const &key -) { - return map->cl->get(map, cx_hash_key_cxstr(key)); -} - -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_cstr_arg(2) -static inline void *cxMapGet( - const CxMap *map, - const char *key -) { - return map->cl->get(map, cx_hash_key_str(key)); -} - -cx_attr_nonnull -static inline int cxMapRemove( - CxMap *map, - CxHashKey const &key -) { - return map->cl->remove(map, key, nullptr); -} - -cx_attr_nonnull -static inline int cxMapRemove( - CxMap *map, - cxstring const &key -) { - return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr); -} - -cx_attr_nonnull -static inline int cxMapRemove( - CxMap *map, - cxmutstr const &key -) { - return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr); -} - -cx_attr_nonnull -cx_attr_cstr_arg(2) -static inline int cxMapRemove( - CxMap *map, - const char *key -) { - return map->cl->remove(map, cx_hash_key_str(key), nullptr); -} - -cx_attr_nonnull -cx_attr_access_w(3) -static inline int cxMapRemoveAndGet( - CxMap *map, - CxHashKey key, - void *targetbuf -) { - return map->cl->remove(map, key, targetbuf); -} - -cx_attr_nonnull -cx_attr_access_w(3) -static inline int cxMapRemoveAndGet( - CxMap *map, - cxstring key, - void *targetbuf -) { - return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf); -} - -cx_attr_nonnull -cx_attr_access_w(3) -static inline int cxMapRemoveAndGet( - CxMap *map, - cxmutstr key, - void *targetbuf -) { - return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf); -} - -cx_attr_nonnull -cx_attr_access_w(3) -cx_attr_cstr_arg(2) -static inline int cxMapRemoveAndGet( - CxMap *map, - const char *key, - void *targetbuf -) { - return map->cl->remove(map, cx_hash_key_str(key), targetbuf); -} - -#else // __cplusplus - -/** - * @copydoc cxMapPut() - */ -cx_attr_nonnull -static inline int cx_map_put( - CxMap *map, - CxHashKey key, - void *value -) { - return map->cl->put(map, key, value); -} - -/** - * @copydoc cxMapPut() - */ -cx_attr_nonnull -static inline int cx_map_put_cxstr( - CxMap *map, - cxstring key, - void *value -) { - return map->cl->put(map, cx_hash_key_cxstr(key), value); -} - -/** - * @copydoc cxMapPut() - */ -cx_attr_nonnull -static inline int cx_map_put_mustr( - CxMap *map, - cxmutstr key, - void *value -) { - return map->cl->put(map, cx_hash_key_cxstr(key), value); -} - -/** - * @copydoc cxMapPut() + * @param map the map + * @param key the key + * @param value the value + * @retval zero success + * @retval non-zero value on memory allocation failure + * @see cxMapPut() */ cx_attr_nonnull -cx_attr_cstr_arg(2) -static inline int cx_map_put_str( - CxMap *map, - const char *key, - void *value -) { - return map->cl->put(map, cx_hash_key_str(key), value); -} +CX_EXPORT int cx_map_put(CxMap *map, CxHashKey key, void *value); /** * Puts a key/value-pair into the map. @@ -594,67 +329,71 @@ static inline int cx_map_put_str( * The @p key is always copied. * * @param map (@c CxMap*) the map - * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key + * @param key (any supported key type) the key * @param value (@c void*) the value * @retval zero success * @retval non-zero value on memory allocation failure + * @see CX_HASH_KEY() */ -#define cxMapPut(map, key, value) _Generic((key), \ - CxHashKey: cx_map_put, \ - cxstring: cx_map_put_cxstr, \ - cxmutstr: cx_map_put_mustr, \ - char*: cx_map_put_str, \ - const char*: cx_map_put_str) \ - (map, key, value) - -/** - * @copydoc cxMapGet() - */ -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cx_map_get( - const CxMap *map, - CxHashKey key -) { - return map->cl->get(map, key); -} +#define cxMapPut(map, key, value) cx_map_put(map, CX_HASH_KEY(key), value) /** - * @copydoc cxMapGet() + * Allocates memory for a value in the map associated with the specified key. + * + * A possible existing value will be overwritten. + * If destructor functions are specified, they are called for + * the overwritten element. + * + * If the map is storing pointers, this function returns a @c void** pointer, + * meaning a pointer to that pointer. + * + * The @p key is always copied. + * + * @param map the map + * @param key the key + * @return the pointer to the allocated memory or @c NULL if allocation fails + * @retval zero success + * @retval non-zero value on memory allocation failure + * @see cxMapEmplace() */ cx_attr_nonnull -cx_attr_nodiscard -static inline void *cx_map_get_cxstr( - const CxMap *map, - cxstring key -) { - return map->cl->get(map, cx_hash_key_cxstr(key)); -} +CX_EXPORT void *cx_map_emplace(CxMap *map, CxHashKey key); /** - * @copydoc cxMapGet() + * Allocates memory for a value in the map associated with the specified key. + * + * A possible existing value will be overwritten. + * If destructor functions are specified, they are called for + * the overwritten element. + * + * If the map is storing pointers, this function returns a @c void** pointer, + * meaning a pointer to that pointer. + * + * The @p key is always copied. + * + * @param map (@c CxMap*) the map + * @param key (any supported key type) the key + * @return the pointer to the allocated memory or @c NULL if allocation fails + * @retval zero success + * @retval non-zero value on memory allocation failure + * @see CX_HASH_KEY() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cx_map_get_mustr( - const CxMap *map, - cxmutstr key -) { - return map->cl->get(map, cx_hash_key_cxstr(key)); -} +#define cxMapEmplace(map, key) cx_map_emplace(map, CX_HASH_KEY(key)) /** - * @copydoc cxMapGet() + * Retrieves a value by using a key. + * + * If this map is storing pointers, the stored pointer is returned. + * Otherwise, a pointer to the element within the map's memory + * is returned (which is valid as long as the element stays in the map). + * + * @param map the map + * @param key the key + * @return the value + * @see cxMapGet() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_cstr_arg(2) -static inline void *cx_map_get_str( - const CxMap *map, - const char *key -) { - return map->cl->get(map, cx_hash_key_str(key)); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT void *cx_map_get(const CxMap *map, CxHashKey key); /** * Retrieves a value by using a key. @@ -664,134 +403,44 @@ static inline void *cx_map_get_str( * is returned (which is valid as long as the element stays in the map). * * @param map (@c CxMap*) the map - * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key + * @param key (any supported key type) the key * @return (@c void*) the value + * @see CX_HASH_KEY() */ -#define cxMapGet(map, key) _Generic((key), \ - CxHashKey: cx_map_get, \ - cxstring: cx_map_get_cxstr, \ - cxmutstr: cx_map_get_mustr, \ - char*: cx_map_get_str, \ - const char*: cx_map_get_str) \ - (map, key) - -/** - * @copydoc cxMapRemove() - */ -cx_attr_nonnull -static inline int cx_map_remove( - CxMap *map, - CxHashKey key -) { - return map->cl->remove(map, key, NULL); -} - -/** - * @copydoc cxMapRemove() - */ -cx_attr_nonnull -static inline int cx_map_remove_cxstr( - CxMap *map, - cxstring key -) { - return map->cl->remove(map, cx_hash_key_cxstr(key), NULL); -} - -/** - * @copydoc cxMapRemove() - */ -cx_attr_nonnull -static inline int cx_map_remove_mustr( - CxMap *map, - cxmutstr key -) { - return map->cl->remove(map, cx_hash_key_cxstr(key), NULL); -} +#define cxMapGet(map, key) cx_map_get(map, CX_HASH_KEY(key)) /** - * @copydoc cxMapRemove() + * Removes a key/value-pair from the map by using the key. + * + * Invokes the destructor functions, if any, on the removed element if and only if the + * @p targetbuf is @c NULL. + * + * @param map the map + * @param key the key + * @param targetbuf the optional buffer where the removed element shall be copied to + * @retval zero success + * @retval non-zero the key was not found + * + * @see cxMapRemove() + * @see cxMapRemoveAndGet() */ -cx_attr_nonnull -cx_attr_cstr_arg(2) -static inline int cx_map_remove_str( - CxMap *map, - const char *key -) { - return map->cl->remove(map, cx_hash_key_str(key), NULL); -} +cx_attr_nonnull_arg(1) +CX_EXPORT int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf); /** * Removes a key/value-pair from the map by using the key. * - * Always invokes the destructors functions, if any, on the removed element. + * Always invokes the destructor functions, if any, on the removed element. * * @param map (@c CxMap*) the map - * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key + * @param key (any supported key type) the key * @retval zero success * @retval non-zero the key was not found * * @see cxMapRemoveAndGet() + * @see CX_HASH_KEY() */ -#define cxMapRemove(map, key) _Generic((key), \ - CxHashKey: cx_map_remove, \ - cxstring: cx_map_remove_cxstr, \ - cxmutstr: cx_map_remove_mustr, \ - char*: cx_map_remove_str, \ - const char*: cx_map_remove_str) \ - (map, key) - -/** - * @copydoc cxMapRemoveAndGet() - */ -cx_attr_nonnull -cx_attr_access_w(3) -static inline int cx_map_remove_and_get( - CxMap *map, - CxHashKey key, - void *targetbuf -) { - return map->cl->remove(map, key, targetbuf); -} - -/** - * @copydoc cxMapRemoveAndGet() - */ -cx_attr_nonnull -cx_attr_access_w(3) -static inline int cx_map_remove_and_get_cxstr( - CxMap *map, - cxstring key, - void *targetbuf -) { - return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf); -} - -/** - * @copydoc cxMapRemoveAndGet() - */ -cx_attr_nonnull -cx_attr_access_w(3) -static inline int cx_map_remove_and_get_mustr( - CxMap *map, - cxmutstr key, - void *targetbuf -) { - return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf); -} - -/** - * @copydoc cxMapRemoveAndGet() - */ -cx_attr_nonnull -cx_attr_access_w(3) -cx_attr_cstr_arg(2) -static inline int cx_map_remove_and_get_str( - CxMap *map, - const char *key, - void *targetbuf -) { - return map->cl->remove(map, cx_hash_key_str(key), targetbuf); -} +#define cxMapRemove(map, key) cx_map_remove(map, CX_HASH_KEY(key), NULL) /** * Removes a key/value-pair from the map by using the key. @@ -805,21 +454,18 @@ static inline int cx_map_remove_and_get_str( * and not the object it points to. * * @param map (@c CxMap*) the map - * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key + * @param key (any supported key type) the key * @param targetbuf (@c void*) the buffer where the element shall be copied to * @retval zero success * @retval non-zero the key was not found * * @see cxMapRemove() + * @see CX_HASH_KEY() */ -#define cxMapRemoveAndGet(map, key, targetbuf) _Generic((key), \ - CxHashKey: cx_map_remove_and_get, \ - cxstring: cx_map_remove_and_get_cxstr, \ - cxmutstr: cx_map_remove_and_get_mustr, \ - char*: cx_map_remove_and_get_str, \ - const char*: cx_map_remove_and_get_str) \ - (map, key, targetbuf) - -#endif // __cplusplus +#define cxMapRemoveAndGet(map, key, targetbuf) cx_map_remove(map, CX_HASH_KEY(key), targetbuf) + +#ifdef __cplusplus +} // extern "C" +#endif #endif // UCX_MAP_H diff --git a/ucx/cx/mempool.h b/ucx/cx/mempool.h index 5d8d284..6530ea7 100644 --- a/ucx/cx/mempool.h +++ b/ucx/cx/mempool.h @@ -156,8 +156,7 @@ typedef struct cx_mempool_s CxMempool; * * @param pool the memory pool to free */ -cx_attr_export -void cxMempoolFree(CxMempool *pool); +CX_EXPORT void cxMempoolFree(CxMempool *pool); /** * Creates an array-based memory pool. @@ -169,11 +168,8 @@ void cxMempoolFree(CxMempool *pool); * @param type the type of memory pool * @return the created memory pool or @c NULL if allocation failed */ -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxMempoolFree, 1) -cx_attr_export -CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type); +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMempoolFree, 1) +CX_EXPORT CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type); /** * Creates a basic array-based memory pool. @@ -212,8 +208,7 @@ CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type); * @param fnc the destructor that shall be applied to all memory blocks */ cx_attr_nonnull_arg(1) -cx_attr_export -void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc); +CX_EXPORT void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc); /** * Sets the global destructor for all memory blocks within the specified pool. @@ -223,8 +218,7 @@ void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc); * @param data additional data for the destructor function */ cx_attr_nonnull_arg(1) -cx_attr_export -void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data); +CX_EXPORT void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data); /** * Sets the destructor function for a specific allocated memory object. @@ -237,11 +231,7 @@ void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void * * @param fnc the destructor function */ cx_attr_nonnull -cx_attr_export -void cxMempoolSetDestructor( - void *memory, - cx_destructor_func fnc -); +CX_EXPORT void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc); /** * Sets the destructor function for a specific allocated memory object. @@ -255,12 +245,7 @@ void cxMempoolSetDestructor( * @param data additional data for the destructor function */ cx_attr_nonnull -cx_attr_export -void cxMempoolSetDestructor2( - void *memory, - cx_destructor_func2 fnc, - void *data -); +CX_EXPORT void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, void *data); /** * Removes the destructor function for a specific allocated memory object. @@ -271,8 +256,7 @@ void cxMempoolSetDestructor2( * @param memory the object allocated in the pool */ cx_attr_nonnull -cx_attr_export -void cxMempoolRemoveDestructor(void *memory); +CX_EXPORT void cxMempoolRemoveDestructor(void *memory); /** * Removes the destructor function for a specific allocated memory object. @@ -283,8 +267,7 @@ void cxMempoolRemoveDestructor(void *memory); * @param memory the object allocated in the pool */ cx_attr_nonnull -cx_attr_export -void cxMempoolRemoveDestructor2(void *memory); +CX_EXPORT void cxMempoolRemoveDestructor2(void *memory); /** * Registers foreign memory with this pool. @@ -302,12 +285,7 @@ void cxMempoolRemoveDestructor2(void *memory); * @retval non-zero failure */ cx_attr_nonnull -cx_attr_export -int cxMempoolRegister( - CxMempool *pool, - void *memory, - cx_destructor_func destr -); +CX_EXPORT int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func destr); /** @@ -330,13 +308,7 @@ int cxMempoolRegister( * @retval non-zero failure */ cx_attr_nonnull -cx_attr_export -int cxMempoolRegister2( - CxMempool *pool, - void *memory, - cx_destructor_func2 destr, - void *data -); +CX_EXPORT int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_func2 destr, void *data); /** * Transfers all the memory managed by one pool to another. @@ -354,11 +326,7 @@ int cxMempoolRegister2( * @retval non-zero allocation failure or incompatible pools */ cx_attr_nonnull -cx_attr_export -int cxMempoolTransfer( - CxMempool *source, - CxMempool *dest -); +CX_EXPORT int cxMempoolTransfer(CxMempool *source, CxMempool *dest); /** * Transfers an object from one pool to another. @@ -375,12 +343,7 @@ int cxMempoolTransfer( * @retval non-zero failure, or the object was not found in the source pool, or the pools are incompatible */ cx_attr_nonnull -cx_attr_export -int cxMempoolTransferObject( - CxMempool *source, - CxMempool *dest, - const void *obj -); +CX_EXPORT int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, const void *obj); #ifdef __cplusplus } // extern "C" diff --git a/ucx/cx/printf.h b/ucx/cx/printf.h index 4ffd05f..7311ab4 100644 --- a/ucx/cx/printf.h +++ b/ucx/cx/printf.h @@ -27,7 +27,7 @@ */ /** * @file printf.h - * @brief Wrapper for write functions with a printf-like interface. + * @brief Wrapper for write-functions with a printf-like interface. * @author Mike Becker * @author Olaf Wintermann * @copyright 2-Clause BSD License @@ -56,8 +56,7 @@ extern "C" { /** * The maximum string length that fits into stack memory. */ -cx_attr_export -extern const unsigned cx_printf_sbo_size; +CX_EXPORT extern const unsigned cx_printf_sbo_size; /** * A @c fprintf like function which writes the output to a stream by @@ -69,16 +68,8 @@ extern const unsigned cx_printf_sbo_size; * @param ... additional arguments * @return the total number of bytes written or an error code from stdlib printf implementation */ -cx_attr_nonnull_arg(1, 2, 3) -cx_attr_printf(3, 4) -cx_attr_cstr_arg(3) -cx_attr_export -int cx_fprintf( - void *stream, - cx_write_func wfc, - const char *fmt, - ... -); +cx_attr_nonnull_arg(1, 2, 3) cx_attr_printf(3, 4) cx_attr_cstr_arg(3) +CX_EXPORT int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...); /** * A @c vfprintf like function which writes the output to a stream by @@ -91,18 +82,11 @@ int cx_fprintf( * @return the total number of bytes written or an error code from stdlib printf implementation * @see cx_fprintf() */ -cx_attr_nonnull -cx_attr_cstr_arg(3) -cx_attr_export -int cx_vfprintf( - void *stream, - cx_write_func wfc, - const char *fmt, - va_list ap -); +cx_attr_nonnull cx_attr_cstr_arg(3) +CX_EXPORT int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_list ap); /** - * A @c asprintf like function which allocates space for a string + * An @c asprintf like function which allocates space for a string * the result is written to. * * @note The resulting string is guaranteed to be zero-terminated, @@ -115,18 +99,11 @@ int cx_vfprintf( * @return the formatted string * @see cx_strfree_a() */ -cx_attr_nonnull_arg(1, 2) -cx_attr_printf(2, 3) -cx_attr_cstr_arg(2) -cx_attr_export -cxmutstr cx_asprintf_a( - const CxAllocator *allocator, - const char *fmt, - ... -); +cx_attr_nonnull_arg(1, 2) cx_attr_printf(2, 3) cx_attr_cstr_arg(2) +CX_EXPORT cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt, ...); /** - * A @c asprintf like function which allocates space for a string + * An @c asprintf like function which allocates space for a string * the result is written to. * * @note The resulting string is guaranteed to be zero-terminated, @@ -138,8 +115,7 @@ cxmutstr cx_asprintf_a( * @return (@c cxmutstr) the formatted string * @see cx_strfree() */ -#define cx_asprintf(fmt, ...) \ - cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__) +#define cx_asprintf(fmt, ...) cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__) /** * A @c vasprintf like function which allocates space for a string @@ -155,21 +131,15 @@ cxmutstr cx_asprintf_a( * @return the formatted string * @see cx_asprintf_a() */ -cx_attr_nonnull -cx_attr_cstr_arg(2) -cx_attr_export -cxmutstr cx_vasprintf_a( - const CxAllocator *allocator, - const char *fmt, - va_list ap -); +cx_attr_nonnull cx_attr_cstr_arg(2) +CX_EXPORT cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt, va_list ap); /** * A @c vasprintf like function which allocates space for a string * the result is written to. * * @note The resulting string is guaranteed to be zero-terminated, - * unless there was in error, in which case the string's pointer + * unless there was an error, in which case the string's pointer * will be @c NULL. * * @param fmt (@c char*) format string @@ -189,8 +159,7 @@ cxmutstr cx_vasprintf_a( * @see cx_fprintf() * @see cxBufferWrite() */ -#define cx_bprintf(buffer, fmt, ...) cx_fprintf((void*)buffer, \ - cxBufferWriteFunc, fmt, __VA_ARGS__) +#define cx_bprintf(buffer, fmt, ...) cx_fprintf((void*)buffer, cxBufferWriteFunc, fmt, __VA_ARGS__) /** @@ -204,7 +173,7 @@ cxmutstr cx_vasprintf_a( * @param len (@c size_t*) a pointer to the length of the buffer * @param fmt (@c char*) the format string * @param ... additional arguments - * @return (@c int) the length of produced string or an error code from stdlib printf implementation + * @return (@c int) the length of the produced string or an error code from stdlib printf implementation */ #define cx_sprintf(str, len, fmt, ...) cx_sprintf_a(cxDefaultAllocator, str, len, fmt, __VA_ARGS__) @@ -222,19 +191,10 @@ cxmutstr cx_vasprintf_a( * @param len a pointer to the length of the buffer * @param fmt the format string * @param ... additional arguments - * @return the length of produced string or an error code from stdlib printf implementation + * @return the length of the produced string or an error code from stdlib printf implementation */ -cx_attr_nonnull_arg(1, 2, 3, 4) -cx_attr_printf(4, 5) -cx_attr_cstr_arg(4) -cx_attr_export -int cx_sprintf_a( - const CxAllocator *alloc, - char **str, - size_t *len, - const char *fmt, - ... -); +cx_attr_nonnull_arg(1, 2, 3, 4) cx_attr_printf(4, 5) cx_attr_cstr_arg(4) +CX_EXPORT int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, ...); /** @@ -248,7 +208,7 @@ int cx_sprintf_a( * @param len (@c size_t*) a pointer to the length of the buffer * @param fmt (@c char*) the format string * @param ap (@c va_list) argument list - * @return (@c int) the length of produced string or an error code from stdlib printf implementation + * @return (@c int) the length of the produced string or an error code from stdlib printf implementation */ #define cx_vsprintf(str, len, fmt, ap) cx_vsprintf_a(cxDefaultAllocator, str, len, fmt, ap) @@ -266,20 +226,10 @@ int cx_sprintf_a( * @param len a pointer to the length of the buffer * @param fmt the format string * @param ap argument list - * @return the length of produced string or an error code from stdlib printf implementation + * @return the length of the produced string or an error code from stdlib printf implementation */ -cx_attr_nonnull -cx_attr_cstr_arg(4) -cx_attr_access_rw(2) -cx_attr_access_rw(3) -cx_attr_export -int cx_vsprintf_a( - const CxAllocator *alloc, - char **str, - size_t *len, - const char *fmt, - va_list ap -); +cx_attr_nonnull cx_attr_cstr_arg(4) cx_attr_access_rw(2) cx_attr_access_rw(3) +CX_EXPORT int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap); /** @@ -300,7 +250,7 @@ int cx_vsprintf_a( * @param str (@c char**) a pointer where the location of the result shall be stored * @param fmt (@c char*) the format string * @param ... additional arguments - * @return (@c int) the length of produced string or an error code from stdlib printf implementation + * @return (@c int) the length of the produced string or an error code from stdlib printf implementation */ #define cx_sprintf_s(buf, len, str, fmt, ...) cx_sprintf_sa(cxDefaultAllocator, buf, len, str, fmt, __VA_ARGS__) @@ -323,23 +273,11 @@ int cx_vsprintf_a( * @param str a pointer where the location of the result shall be stored * @param fmt the format string * @param ... additional arguments - * @return the length of produced string or an error code from stdlib printf implementation + * @return the length of the produced string or an error code from stdlib printf implementation */ -cx_attr_nonnull_arg(1, 2, 4, 5) -cx_attr_printf(5, 6) -cx_attr_cstr_arg(5) -cx_attr_access_rw(2) -cx_attr_access_rw(3) -cx_attr_access_rw(4) -cx_attr_export -int cx_sprintf_sa( - const CxAllocator *alloc, - char *buf, - size_t *len, - char **str, - const char *fmt, - ... -); +cx_attr_nonnull_arg(1, 2, 4, 5) cx_attr_printf(5, 6) cx_attr_cstr_arg(5) +cx_attr_access_rw(2) cx_attr_access_rw(3) cx_attr_access_rw(4) +CX_EXPORT int cx_sprintf_sa(const 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. @@ -359,7 +297,7 @@ int cx_sprintf_sa( * @param str (@c char**) a pointer where the location of the result shall be stored * @param fmt (@c char*) the format string * @param ap (@c va_list) argument list - * @return (@c int) the length of produced string or an error code from stdlib printf implementation + * @return (@c int) the length of the produced string or an error code from stdlib printf implementation */ #define cx_vsprintf_s(buf, len, str, fmt, ap) cx_vsprintf_sa(cxDefaultAllocator, buf, len, str, fmt, ap) @@ -382,19 +320,10 @@ int cx_sprintf_sa( * @param str a pointer where the location of the result shall be stored * @param fmt the format string * @param ap argument list - * @return the length of produced string or an error code from stdlib printf implementation + * @return the length of the produced string or an error code from stdlib printf implementation */ -cx_attr_nonnull -cx_attr_cstr_arg(5) -cx_attr_export -int cx_vsprintf_sa( - const CxAllocator *alloc, - char *buf, - size_t *len, - char **str, - const char *fmt, - va_list ap -); +cx_attr_nonnull cx_attr_cstr_arg(5) +CX_EXPORT int cx_vsprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap); #ifdef __cplusplus diff --git a/ucx/cx/properties.h b/ucx/cx/properties.h index d6df8f5..95a4bbe 100644 --- a/ucx/cx/properties.h +++ b/ucx/cx/properties.h @@ -94,8 +94,7 @@ typedef struct cx_properties_config_s CxPropertiesConfig; /** * Default properties configuration. */ -cx_attr_export -extern const CxPropertiesConfig cx_properties_config_default; +CX_EXPORT extern const CxPropertiesConfig cx_properties_config_default; /** * Status codes for the properties interface. @@ -122,7 +121,7 @@ enum cx_properties_status { * You can use this enumerator to check for all "good" status results * by checking if the status is less than @c CX_PROPERTIES_OK. * - * A "good" status means, that you can refill data and continue parsing. + * A "good" status means that you can refill data and continue parsing. */ CX_PROPERTIES_OK, /** @@ -130,11 +129,11 @@ enum cx_properties_status { */ CX_PROPERTIES_NULL_INPUT, /** - * The line contains a delimiter, but no key. + * The line contains a delimiter but no key. */ CX_PROPERTIES_INVALID_EMPTY_KEY, /** - * The line contains data, but no delimiter. + * The line contains data but no delimiter. */ CX_PROPERTIES_INVALID_MISSING_DELIMITER, /** @@ -200,7 +199,7 @@ typedef struct cx_properties_sink_s CxPropertiesSink; /** * A function that consumes a k/v-pair in a sink. * - * The sink could be e.g. a map and the sink function would be calling + * The sink could be a map, and the sink function would be calling * a map function to store the k/v-pair. * * @param prop the properties interface that wants to sink a k/v-pair @@ -210,7 +209,6 @@ typedef struct cx_properties_sink_s CxPropertiesSink; * @retval zero success * @retval non-zero sinking the k/v-pair failed */ -cx_attr_nonnull typedef int(*cx_properties_sink_func)( CxProperties *prop, CxPropertiesSink *sink, @@ -257,7 +255,6 @@ typedef struct cx_properties_source_s CxPropertiesSource; * @retval zero success * @retval non-zero reading the data failed */ -cx_attr_nonnull typedef int(*cx_properties_read_func)( CxProperties *prop, CxPropertiesSource *src, @@ -272,7 +269,6 @@ typedef int(*cx_properties_read_func)( * @retval zero initialization was successful * @retval non-zero otherwise */ -cx_attr_nonnull typedef int(*cx_properties_read_init_func)( CxProperties *prop, CxPropertiesSource *src @@ -284,7 +280,6 @@ typedef int(*cx_properties_read_init_func)( * @param prop the properties interface that wants to read from the source * @param src the source */ -cx_attr_nonnull typedef void(*cx_properties_read_clean_func)( CxProperties *prop, CxPropertiesSource *src @@ -297,7 +292,7 @@ struct cx_properties_source_s { /** * The source object. * - * For example a file stream or a string. + * For example, a file stream or a string. */ void *src; /** @@ -331,38 +326,32 @@ struct cx_properties_source_s { * @see cxPropertiesInitDefault() */ cx_attr_nonnull -cx_attr_export -void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config); +CX_EXPORT void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config); /** * Destroys the properties interface. * * @note Even when you are certain that you did not use the interface in a * way that caused a memory allocation, you should call this function anyway. - * Future versions of the library might add features that need additional memory - * and you really don't want to search the entire code where you might need - * add call to this function. + * Future versions of the library might add features that need additional memory, + * and you really don't want to search the entire code where you might need to + * add a call to this function. * * @param prop the properties interface */ cx_attr_nonnull -cx_attr_export -void cxPropertiesDestroy(CxProperties *prop); +CX_EXPORT void cxPropertiesDestroy(CxProperties *prop); /** * Destroys and re-initializes the properties interface. * - * You might want to use this, to reset the parser after + * You might want to use this to reset the parser after * encountering a syntax error. * * @param prop the properties interface */ cx_attr_nonnull -static inline void cxPropertiesReset(CxProperties *prop) { - CxPropertiesConfig config = prop->config; - cxPropertiesDestroy(prop); - cxPropertiesInit(prop, config); -} +CX_EXPORT void cxPropertiesReset(CxProperties *prop); /** * Initialize a properties parser with the default configuration. @@ -371,7 +360,7 @@ static inline void cxPropertiesReset(CxProperties *prop) { * @see cxPropertiesInit() */ #define cxPropertiesInitDefault(prop) \ - cxPropertiesInit(prop, cx_properties_config_default) + cxPropertiesInit(prop, cx_properties_config_default) /** * Fills the input buffer with data. @@ -394,44 +383,22 @@ static inline void cxPropertiesReset(CxProperties *prop) { * @retval non-zero a memory allocation was necessary but failed * @see cxPropertiesFill() */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -int cxPropertiesFilln( - CxProperties *prop, - const char *buf, - size_t len -); - -#ifdef __cplusplus -} // extern "C" -cx_attr_nonnull -static inline int cxPropertiesFill( - CxProperties *prop, - cxstring str -) { - return cxPropertiesFilln(prop, str.ptr, str.length); -} +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len); +/** + * Internal function, do not use. + * + * @param prop the properties interface + * @param str the text to fill in + * @retval zero success + * @retval non-zero a memory allocation was necessary but failed + */ cx_attr_nonnull -static inline int cxPropertiesFill( - CxProperties *prop, - cxmutstr str -) { +CX_INLINE int cx_properties_fill(CxProperties *prop, cxstring str) { return cxPropertiesFilln(prop, str.ptr, str.length); } -cx_attr_nonnull -cx_attr_cstr_arg(2) -static inline int cxPropertiesFill( - CxProperties *prop, - const char *str -) { - return cxPropertiesFilln(prop, str, strlen(str)); -} - -extern "C" { -#else // __cplusplus /** * Fills the input buffer with data. * @@ -452,62 +419,17 @@ extern "C" { * @retval non-zero a memory allocation was necessary but failed * @see cxPropertiesFilln() */ -#define cxPropertiesFill(prop, str) _Generic((str), \ - cxstring: cx_properties_fill_cxstr, \ - cxmutstr: cx_properties_fill_mutstr, \ - char*: cx_properties_fill_str, \ - const char*: cx_properties_fill_str) \ - (prop, str) +#define cxPropertiesFill(prop, str) cx_properties_fill(prop, cx_strcast(str)) /** - * @copydoc cxPropertiesFill() - */ -cx_attr_nonnull -static inline int cx_properties_fill_cxstr( - CxProperties *prop, - cxstring str -) { - return cxPropertiesFilln(prop, str.ptr, str.length); -} - -/** - * @copydoc cxPropertiesFill() - */ -cx_attr_nonnull -static inline int cx_properties_fill_mutstr( - CxProperties *prop, - cxmutstr str -) { - return cxPropertiesFilln(prop, str.ptr, str.length); -} - -/** - * @copydoc cxPropertiesFill() - */ -cx_attr_nonnull -cx_attr_cstr_arg(2) -static inline int cx_properties_fill_str( - CxProperties *prop, - const char *str -) { - return cxPropertiesFilln(prop, str, strlen(str)); -} -#endif - -/** - * Specifies stack memory that shall be used as internal buffer. + * Specifies stack memory that shall be used as an internal buffer. * * @param prop the properties interface * @param buf a pointer to stack memory * @param capacity the capacity of the stack memory */ cx_attr_nonnull -cx_attr_export -void cxPropertiesUseStack( - CxProperties *prop, - char *buf, - size_t capacity -); +CX_EXPORT void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capacity); /** * Retrieves the next key/value-pair. @@ -539,14 +461,8 @@ void cxPropertiesUseStack( * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxPropertiesStatus cxPropertiesNext( - CxProperties *prop, - cxstring *key, - cxstring *value -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value); /** * Creates a properties sink for an UCX map. @@ -562,10 +478,8 @@ CxPropertiesStatus cxPropertiesNext( * @return the sink * @see cxPropertiesLoad() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxPropertiesSink cxPropertiesMapSink(CxMap *map); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxPropertiesSink cxPropertiesMapSink(CxMap *map); /** * Creates a properties source based on an UCX string. @@ -575,8 +489,7 @@ CxPropertiesSink cxPropertiesMapSink(CxMap *map); * @see cxPropertiesLoad() */ cx_attr_nodiscard -cx_attr_export -CxPropertiesSource cxPropertiesStringSource(cxstring str); +CX_EXPORT CxPropertiesSource cxPropertiesStringSource(cxstring str); /** * Creates a properties source based on C string with the specified length. @@ -586,11 +499,8 @@ CxPropertiesSource cxPropertiesStringSource(cxstring str); * @return the properties source * @see cxPropertiesLoad() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_access_r(1, 2) -cx_attr_export -CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len); +cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1, 2) +CX_EXPORT CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len); /** * Creates a properties source based on a C string. @@ -602,11 +512,8 @@ CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len); * @return the properties source * @see cxPropertiesLoad() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_export -CxPropertiesSource cxPropertiesCstrSource(const char *str); +cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT CxPropertiesSource cxPropertiesCstrSource(const char *str); /** * Creates a properties source based on an FILE. @@ -617,11 +524,8 @@ CxPropertiesSource cxPropertiesCstrSource(const char *str); * @return the properties source * @see cxPropertiesLoad() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_access_r(1) -cx_attr_export -CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size); +cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1) +CX_EXPORT CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size); /** @@ -653,12 +557,8 @@ CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size); * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed */ cx_attr_nonnull -cx_attr_export -CxPropertiesStatus cxPropertiesLoad( - CxProperties *prop, - CxPropertiesSink sink, - CxPropertiesSource source -); +CX_EXPORT CxPropertiesStatus cxPropertiesLoad(CxProperties *prop, + CxPropertiesSink sink, CxPropertiesSource source); #ifdef __cplusplus } // extern "C" diff --git a/ucx/cx/streams.h b/ucx/cx/streams.h index 3cc35fa..298ca3a 100644 --- a/ucx/cx/streams.h +++ b/ucx/cx/streams.h @@ -54,7 +54,7 @@ extern "C" { * @param wfnc the write function * @param buf a pointer to the copy buffer or @c NULL if a buffer * shall be implicitly created on the heap - * @param bufsize the size of the copy buffer - if @p buf is @c NULL you can + * @param bufsize the size of the copy buffer - if @p buf is @c NULL, you can * set this to zero to let the implementation decide * @param n the maximum number of bytes that shall be copied. * If this is larger than @p bufsize, the content is copied over multiple @@ -62,19 +62,10 @@ extern "C" { * @return the total number of bytes copied */ cx_attr_nonnull_arg(1, 2, 3, 4) -cx_attr_access_r(1) -cx_attr_access_w(2) -cx_attr_access_w(5) -cx_attr_export -size_t cx_stream_bncopy( - void *src, - void *dest, - cx_read_func rfnc, - cx_write_func wfnc, - char *buf, - size_t bufsize, - size_t n -); +cx_attr_access_r(1) cx_attr_access_w(2) cx_attr_access_w(5) +CX_EXPORT size_t cx_stream_bncopy(void *src, void *dest, + cx_read_func rfnc, cx_write_func wfnc, + char *buf, size_t bufsize, size_t n); /** * Reads data from a stream and writes it to another stream. @@ -86,7 +77,7 @@ size_t cx_stream_bncopy( * @param buf (@c char*) a pointer to the copy buffer or @c NULL if a buffer * shall be implicitly created on the heap * @param bufsize (@c size_t) the size of the copy buffer - if @p buf is - * @c NULL you can set this to zero to let the implementation decide + * @c NULL, you can set this to zero to let the implementation decide * @return total number of bytes copied */ #define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \ @@ -95,7 +86,7 @@ size_t cx_stream_bncopy( /** * Reads data from a stream and writes it to another stream. * - * The data is temporarily stored in a stack allocated buffer. + * The data is temporarily stored in a stack-allocated buffer. * * @param src the source stream * @param dest the destination stream @@ -104,22 +95,14 @@ size_t cx_stream_bncopy( * @param n the maximum number of bytes that shall be copied. * @return total number of bytes copied */ -cx_attr_nonnull -cx_attr_access_r(1) -cx_attr_access_w(2) -cx_attr_export -size_t cx_stream_ncopy( - void *src, - void *dest, - cx_read_func rfnc, - cx_write_func wfnc, - size_t n -); +cx_attr_nonnull cx_attr_access_r(1) cx_attr_access_w(2) +CX_EXPORT size_t cx_stream_ncopy(void *src, void *dest, + cx_read_func rfnc, cx_write_func wfnc, size_t n); /** * Reads data from a stream and writes it to another stream. * - * The data is temporarily stored in a stack allocated buffer. + * The data is temporarily stored in a stack-allocated buffer. * * @param src (@c void*) the source stream * @param dest (@c void*) the destination stream diff --git a/ucx/cx/string.h b/ucx/cx/string.h index 41bf18a..5177ceb 100644 --- a/ucx/cx/string.h +++ b/ucx/cx/string.h @@ -48,8 +48,7 @@ /** * The maximum length of the "needle" in cx_strstr() that can use SBO. */ -cx_attr_export -extern const unsigned cx_strstr_sbo_size; +CX_EXPORT extern const unsigned cx_strstr_sbo_size; /** * The UCX string structure. @@ -112,10 +111,10 @@ struct cx_strtok_ctx_s { */ size_t pos; /** - * Position of next delimiter in the source string. + * Position of the next delimiter in the source string. * * If the tokenizer has not yet returned a token, the content of this field - * is undefined. If the tokenizer reached the end of the string, this field + * is undefined. If the tokenizer reaches the end of the string, this field * contains the length of the source string. */ size_t delim_pos; @@ -167,21 +166,20 @@ extern "C" { * * The length is implicitly inferred by using a call to @c strlen(). * + * When @c NULL is passed, the length will be set to zero. + * * @note the wrapped string will share the specified pointer to the string. * If you do want a copy, use cx_strdup() on the return value of this function. * * If you need to wrap a constant string, use cx_str(). * - * @param cstring the string to wrap, must be zero-terminated + * @param cstring the string to wrap (must be zero-terminated) * @return the wrapped string * * @see cx_mutstrn() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_export -cxmutstr cx_mutstr(char *cstring); +cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT cxmutstr cx_mutstr(char *cstring); /** * Wraps a string that does not need to be zero-terminated. @@ -199,34 +197,28 @@ cxmutstr cx_mutstr(char *cstring); * * @see cx_mutstr() */ -cx_attr_nodiscard -cx_attr_access_rw(1, 2) -cx_attr_export -cxmutstr cx_mutstrn( - char *cstring, - size_t length -); +cx_attr_nodiscard cx_attr_access_rw(1, 2) +CX_EXPORT cxmutstr cx_mutstrn(char *cstring, size_t length); /** * Wraps a string that must be zero-terminated. * * The length is implicitly inferred by using a call to @c strlen(). * + * When @c NULL is passed, the length will be set to zero. + * * @note the wrapped string will share the specified pointer to the string. * If you do want a copy, use cx_strdup() on the return value of this function. * * If you need to wrap a non-constant string, use cx_mutstr(). * - * @param cstring the string to wrap, must be zero-terminated + * @param cstring the string to wrap (must be zero-terminated) * @return the wrapped string * * @see cx_strn() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_export -cxstring cx_str(const char *cstring); +cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT cxstring cx_str(const char *cstring); /** @@ -245,28 +237,27 @@ cxstring cx_str(const char *cstring); * * @see cx_str() */ -cx_attr_nodiscard -cx_attr_access_r(1, 2) -cx_attr_export -cxstring cx_strn( - const char *cstring, - size_t length -); +cx_attr_nodiscard cx_attr_access_r(1, 2) +CX_EXPORT cxstring cx_strn(const char *cstring, size_t length); #ifdef __cplusplus } // extern "C" cx_attr_nodiscard -static inline cxstring cx_strcast(cxmutstr str) { +CX_CPPDECL cxstring cx_strcast(cxmutstr str) { return cx_strn(str.ptr, str.length); } cx_attr_nodiscard -static inline cxstring cx_strcast(cxstring str) { +CX_CPPDECL cxstring cx_strcast(cxstring str) { return str; } cx_attr_nodiscard -static inline cxstring cx_strcast(const char *str) { +CX_CPPDECL cxstring cx_strcast(const char *str) { return cx_str(str); } +cx_attr_nodiscard +CX_CPPDECL cxstring cx_strcast(const unsigned char *str) { + return cx_str(static_cast(str)); +} extern "C" { #else /** @@ -276,7 +267,7 @@ extern "C" { * @see cx_strcast() */ cx_attr_nodiscard -static inline cxstring cx_strcast_m(cxmutstr str) { +CX_INLINE cxstring cx_strcast_m(cxmutstr str) { return (cxstring) {str.ptr, str.length}; } /** @@ -286,7 +277,7 @@ static inline cxstring cx_strcast_m(cxmutstr str) { * @see cx_strcast() */ cx_attr_nodiscard -static inline cxstring cx_strcast_c(cxstring str) { +CX_INLINE cxstring cx_strcast_c(cxstring str) { return str; } @@ -297,25 +288,32 @@ static inline cxstring cx_strcast_c(cxstring str) { * @see cx_strcast() */ cx_attr_nodiscard -static inline cxstring cx_strcast_z(const char *str) { +CX_INLINE cxstring cx_strcast_u(const unsigned char *str) { + return cx_str((const char*)str); +} + +/** + * Internal function, do not use. + * @param str + * @return + * @see cx_strcast() + */ +cx_attr_nodiscard +CX_INLINE cxstring cx_strcast_z(const char *str) { return cx_str(str); } /** -* Casts a mutable string to an immutable string. -* -* Does nothing for already immutable strings. -* -* @note This is not seriously a cast. Instead, you get a copy -* of the struct with the desired pointer type. Both structs still -* point to the same location, though! -* -* @param str (@c cxstring or @c cxmutstr) the string to cast -* @return (@c cxstring) an immutable copy of the string pointer -*/ + * Wraps any string into an UCX string. + * + * @param str (any supported string type) the string to cast + * @return (@c cxstring) the string wrapped as UCX string + */ #define cx_strcast(str) _Generic((str), \ cxmutstr: cx_strcast_m, \ cxstring: cx_strcast_c, \ + const unsigned char*: cx_strcast_u, \ + unsigned char *: cx_strcast_u, \ const char*: cx_strcast_z, \ char *: cx_strcast_z) (str) #endif @@ -323,37 +321,32 @@ static inline cxstring cx_strcast_z(const char *str) { /** * Passes the pointer in this string to the cxDefaultAllocator's @c free() function. * - * The pointer in the struct is set to @c NULL and the length is set to zero + * The pointer in the struct is set to @c NULL, and the length is set to zero, * which means that this function protects you against double-free. * * @note There is no implementation for cxstring, because it is unlikely that * you ever have a const char* you are really supposed to free. - * If you encounter such situation, you should double-check your code. + * If you encounter such a situation, you should double-check your code. * * @param str the string to free */ -cx_attr_export -void cx_strfree(cxmutstr *str); +CX_EXPORT void cx_strfree(cxmutstr *str); /** - * Passes the pointer in this string to the allocators free function. + * Passes the pointer in this string to the allocator's free function. * - * The pointer in the struct is set to @c NULL and the length is set to zero + * The pointer in the struct is set to @c NULL, and the length is set to zero, * which means that this function protects you against double-free. * * @note There is no implementation for cxstring, because it is unlikely that * you ever have a const char* you are really supposed to free. - * If you encounter such situation, you should double-check your code. + * If you encounter such a situation, you should double-check your code. * * @param alloc the allocator * @param str the string to free */ cx_attr_nonnull_arg(1) -cx_attr_export -void cx_strfree_a( - const CxAllocator *alloc, - cxmutstr *str -); +CX_EXPORT void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str); /** * Copies a string. @@ -371,12 +364,7 @@ void cx_strfree_a( * @retval non-zero if re-allocation failed */ cx_attr_nonnull_arg(1) -cx_attr_export -int cx_strcpy_a( - const CxAllocator *alloc, - cxmutstr *dest, - cxstring src -); +CX_EXPORT int cx_strcpy_a(const CxAllocator *alloc, cxmutstr *dest, cxstring src); /** @@ -408,11 +396,7 @@ int cx_strcpy_a( * @return the accumulated length of all strings */ cx_attr_nodiscard -cx_attr_export -size_t cx_strlen( - size_t count, - ... -); +CX_EXPORT size_t cx_strlen(size_t count, ...); /** * Concatenates strings. @@ -436,15 +420,9 @@ size_t cx_strlen( * @param ... all other UCX strings * @return the concatenated string */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -cxmutstr cx_strcat_ma( - const CxAllocator *alloc, - cxmutstr str, - size_t count, - ... -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT cxmutstr cx_strcat_ma(const CxAllocator *alloc, + cxmutstr str, size_t count, ...); /** * Concatenates strings and returns a new string. @@ -465,7 +443,7 @@ cxmutstr cx_strcat_ma( * @return (@c cxmutstr) the concatenated string */ #define cx_strcat_a(alloc, count, ...) \ -cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__) /** * Concatenates strings and returns a new string. @@ -485,7 +463,7 @@ cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__) * @return (@c cxmutstr) the concatenated string */ #define cx_strcat(count, ...) \ -cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__) /** * Concatenates strings. @@ -509,7 +487,7 @@ cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__) * @return (@c cxmutstr) the concatenated string */ #define cx_strcat_m(str, count, ...) \ -cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__) + cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__) /** * Returns a substring starting at the specified location. @@ -527,11 +505,7 @@ cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__) * @see cx_strsubsl_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strsubs( - cxstring string, - size_t start -); +CX_EXPORT cxstring cx_strsubs(cxstring string, size_t start); /** * Returns a substring starting at the specified location. @@ -553,12 +527,7 @@ cxstring cx_strsubs( * @see cx_strsubsl_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strsubsl( - cxstring string, - size_t start, - size_t length -); +CX_EXPORT cxstring cx_strsubsl(cxstring string, size_t start, size_t length); /** * Returns a substring starting at the specified location. @@ -576,11 +545,7 @@ cxstring cx_strsubsl( * @see cx_strsubsl() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strsubs_m( - cxmutstr string, - size_t start -); +CX_EXPORT cxmutstr cx_strsubs_m(cxmutstr string, size_t start); /** * Returns a substring starting at the specified location. @@ -602,12 +567,7 @@ cxmutstr cx_strsubs_m( * @see cx_strsubsl() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strsubsl_m( - cxmutstr string, - size_t start, - size_t length -); +CX_EXPORT cxmutstr cx_strsubsl_m(cxmutstr string, size_t start, size_t length); /** * Returns a substring starting at the location of the first occurrence of the @@ -622,11 +582,7 @@ cxmutstr cx_strsubsl_m( * @see cx_strchr_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strchr( - cxstring string, - int chr -); +CX_EXPORT cxstring cx_strchr(cxstring string, int chr); /** * Returns a substring starting at the location of the first occurrence of the @@ -641,11 +597,7 @@ cxstring cx_strchr( * @see cx_strchr() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strchr_m( - cxmutstr string, - int chr -); +CX_EXPORT cxmutstr cx_strchr_m(cxmutstr string, int chr); /** * Returns a substring starting at the location of the last occurrence of the @@ -660,11 +612,7 @@ cxmutstr cx_strchr_m( * @see cx_strrchr_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strrchr( - cxstring string, - int chr -); +CX_EXPORT cxstring cx_strrchr(cxstring string, int chr); /** * Returns a substring starting at the location of the last occurrence of the @@ -679,11 +627,7 @@ cxstring cx_strrchr( * @see cx_strrchr() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strrchr_m( - cxmutstr string, - int chr -); +CX_EXPORT cxmutstr cx_strrchr_m(cxmutstr string, int chr); /** * Returns a substring starting at the location of the first occurrence of the @@ -702,11 +646,7 @@ cxmutstr cx_strrchr_m( * @see cx_strstr_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strstr( - cxstring haystack, - cxstring needle -); +CX_EXPORT cxstring cx_strstr(cxstring haystack, cxstring needle); /** * Returns a substring starting at the location of the first occurrence of the @@ -725,11 +665,7 @@ cxstring cx_strstr( * @see cx_strstr() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strstr_m( - cxmutstr haystack, - cxstring needle -); +CX_EXPORT cxmutstr cx_strstr_m(cxmutstr haystack, cxstring needle); /** * Splits a given string using a delimiter string. @@ -743,16 +679,9 @@ cxmutstr cx_strstr_m( * @param output a preallocated array of at least @p limit length * @return the actual number of split items */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_access_w(4, 3) -cx_attr_export -size_t cx_strsplit( - cxstring string, - cxstring delim, - size_t limit, - cxstring *output -); +cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3) +CX_EXPORT size_t cx_strsplit(cxstring string, cxstring delim, + size_t limit, cxstring *output); /** * Splits a given string using a delimiter string. @@ -773,17 +702,10 @@ size_t cx_strsplit( * written to * @return the actual number of split items */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_access_w(5) -cx_attr_export -size_t cx_strsplit_a( - const CxAllocator *allocator, - cxstring string, - cxstring delim, - size_t limit, - cxstring **output -); +cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5) +CX_EXPORT size_t cx_strsplit_a(const CxAllocator *allocator, + cxstring string, cxstring delim, + size_t limit, cxstring **output); /** @@ -798,16 +720,9 @@ size_t cx_strsplit_a( * @param output a preallocated array of at least @p limit length * @return the actual number of split items */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_access_w(4, 3) -cx_attr_export -size_t cx_strsplit_m( - cxmutstr string, - cxstring delim, - size_t limit, - cxmutstr *output -); +cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3) +CX_EXPORT size_t cx_strsplit_m(cxmutstr string, cxstring delim, + size_t limit, cxmutstr *output); /** * Splits a given string using a delimiter string. @@ -828,17 +743,10 @@ size_t cx_strsplit_m( * written to * @return the actual number of split items */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_access_w(5) -cx_attr_export -size_t cx_strsplit_ma( - const CxAllocator *allocator, - cxmutstr string, - cxstring delim, - size_t limit, - cxmutstr **output -); +cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5) +CX_EXPORT size_t cx_strsplit_ma(const CxAllocator *allocator, + cxmutstr string, cxstring delim, size_t limit, + cxmutstr **output); /** * Compares two strings. @@ -849,11 +757,17 @@ size_t cx_strsplit_ma( * than @p s2, zero if both strings equal */ cx_attr_nodiscard -cx_attr_export -int cx_strcmp( - cxstring s1, - cxstring s2 -); +CX_EXPORT int cx_strcmp_(cxstring s1, cxstring s2); + +/** + * Compares two strings. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger + * than @p s2, zero if both strings equal + */ +#define cx_strcmp(s1, s2) cx_strcmp_(cx_strcast(s1), cx_strcast(s2)) /** * Compares two strings ignoring case. @@ -864,29 +778,33 @@ int cx_strcmp( * than @p s2, zero if both strings equal ignoring case */ cx_attr_nodiscard -cx_attr_export -int cx_strcasecmp( - cxstring s1, - cxstring s2 -); +CX_EXPORT int cx_strcasecmp_(cxstring s1, cxstring s2); + +/** + * Compares two strings ignoring case. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger + * than @p s2, zero if both strings equal ignoring case + */ +#define cx_strcasecmp(s1, s2) cx_strcasecmp_(cx_strcast(s1), cx_strcast(s2)) /** * Compares two strings. * * This function has a compatible signature for the use as a cx_compare_func. * + * @attention This function can @em only compare UCX strings. It is unsafe to + * pass normal C-strings to this function. + * * @param s1 the first string * @param s2 the second string * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger * than @p s2, zero if both strings equal */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -int cx_strcmp_p( - const void *s1, - const void *s2 -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cx_strcmp_p(const void *s1, const void *s2); /** * Compares two strings ignoring case. @@ -898,13 +816,8 @@ int cx_strcmp_p( * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger * than @p s2, zero if both strings equal ignoring case */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -int cx_strcasecmp_p( - const void *s1, - const void *s2 -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cx_strcasecmp_p(const void *s1, const void *s2); /** @@ -919,13 +832,8 @@ int cx_strcasecmp_p( * @return a duplicate of the string * @see cx_strdup() */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -cxmutstr cx_strdup_a_( - const CxAllocator *allocator, - cxstring string -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string); /** * Creates a duplicate of the specified string. @@ -940,8 +848,7 @@ cxmutstr cx_strdup_a_( * @see cx_strdup() * @see cx_strfree_a() */ -#define cx_strdup_a(allocator, string) \ - cx_strdup_a_((allocator), cx_strcast(string)) +#define cx_strdup_a(allocator, string) cx_strdup_a_((allocator), cx_strcast(string)) /** * Creates a duplicate of the specified string. @@ -968,8 +875,7 @@ cxmutstr cx_strdup_a_( * @return the trimmed string */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strtrim(cxstring string); +CX_EXPORT cxstring cx_strtrim(cxstring string); /** * Omits leading and trailing spaces. @@ -981,11 +887,10 @@ cxstring cx_strtrim(cxstring string); * @return the trimmed string */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strtrim_m(cxmutstr string); +CX_EXPORT cxmutstr cx_strtrim_m(cxmutstr string); /** - * Checks, if a string has a specific prefix. + * Checks if a string has a specific prefix. * * @param string the string to check * @param prefix the prefix the string should have @@ -993,14 +898,20 @@ cxmutstr cx_strtrim_m(cxmutstr string); * @c false otherwise */ cx_attr_nodiscard -cx_attr_export -bool cx_strprefix( - cxstring string, - cxstring prefix -); +CX_EXPORT bool cx_strprefix_(cxstring string, cxstring prefix); /** - * Checks, if a string has a specific suffix. + * Checks if a string has a specific prefix. + * + * @param string the string to check + * @param prefix the prefix the string should have + * @return @c true, if and only if the string has the specified prefix, + * @c false otherwise + */ +#define cx_strprefix(string, prefix) cx_strprefix_(cx_strcast(string), cx_strcast(prefix)) + +/** + * Checks if a string has a specific suffix. * * @param string the string to check * @param suffix the suffix the string should have @@ -1008,14 +919,20 @@ bool cx_strprefix( * @c false otherwise */ cx_attr_nodiscard -cx_attr_export -bool cx_strsuffix( - cxstring string, - cxstring suffix -); +CX_EXPORT bool cx_strsuffix_(cxstring string, cxstring suffix); + +/** + * Checks if a string has a specific suffix. + * + * @param string the string to check + * @param suffix the suffix the string should have + * @return @c true, if and only if the string has the specified suffix, + * @c false otherwise + */ +#define cx_strsuffix(string, suffix) cx_strsuffix_(cx_strcast(string), cx_strcast(suffix)) /** - * Checks, if a string has a specific prefix, ignoring the case. + * Checks if a string has a specific prefix, ignoring the case. * * @param string the string to check * @param prefix the prefix the string should have @@ -1023,11 +940,17 @@ bool cx_strsuffix( * @c false otherwise */ cx_attr_nodiscard -cx_attr_export -bool cx_strcaseprefix( - cxstring string, - cxstring prefix -); +CX_EXPORT bool cx_strcaseprefix_(cxstring string, cxstring prefix); + +/** + * Checks if a string has a specific prefix, ignoring the case. + * + * @param string the string to check + * @param prefix the prefix the string should have + * @return @c true, if and only if the string has the specified prefix, + * @c false otherwise + */ +#define cx_strcaseprefix(string, prefix) cx_strcaseprefix_(cx_strcast(string), cx_strcast(prefix)) /** * Checks, if a string has a specific suffix, ignoring the case. @@ -1038,16 +961,22 @@ bool cx_strcaseprefix( * @c false otherwise */ cx_attr_nodiscard -cx_attr_export -bool cx_strcasesuffix( - cxstring string, - cxstring suffix -); +CX_EXPORT bool cx_strcasesuffix_(cxstring string, cxstring suffix); + +/** + * Checks, if a string has a specific suffix, ignoring the case. + * + * @param string the string to check + * @param suffix the suffix the string should have + * @return @c true, if and only if the string has the specified suffix, + * @c false otherwise + */ +#define cx_strcasesuffix(string, suffix) cx_strcasesuffix_(cx_strcast(string), cx_strcast(suffix)) /** * Replaces a string with another string. * - * Replaces at most @p replmax occurrences. + * The function replaces at most @p replmax occurrences. * * The returned string will be allocated by @p allocator and is guaranteed * to be zero-terminated. @@ -1062,21 +991,14 @@ bool cx_strcasesuffix( * @param replmax maximum number of replacements * @return the resulting string after applying the replacements */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -cxmutstr cx_strreplacen_a( - const CxAllocator *allocator, - cxstring str, - cxstring search, - cxstring replacement, - size_t replmax -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT cxmutstr cx_strreplacen_a(const CxAllocator *allocator, + cxstring str, cxstring search, cxstring replacement, size_t replmax); /** * Replaces a string with another string. * - * Replaces at most @p replmax occurrences. + * The function replaces at most @p replmax occurrences. * * The returned string will be allocated by the cxDefaultAllocator and is guaranteed * to be zero-terminated. @@ -1091,7 +1013,7 @@ cxmutstr cx_strreplacen_a( * @return (@c cxmutstr) the resulting string after applying the replacements */ #define cx_strreplacen(str, search, replacement, replmax) \ -cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, replmax) + cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, replmax) /** * Replaces a string with another string. @@ -1109,7 +1031,7 @@ cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, replmax) * @return (@c cxmutstr) the resulting string after applying the replacements */ #define cx_strreplace_a(allocator, str, search, replacement) \ -cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX) + cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX) /** * Replaces a string with another string. @@ -1126,7 +1048,7 @@ cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX) * @return (@c cxmutstr) the resulting string after applying the replacements */ #define cx_strreplace(str, search, replacement) \ -cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, SIZE_MAX) + cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, SIZE_MAX) /** * Creates a string tokenization context. @@ -1137,12 +1059,7 @@ cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, SIZE_MAX) * @return a new string tokenization context */ cx_attr_nodiscard -cx_attr_export -CxStrtokCtx cx_strtok_( - cxstring str, - cxstring delim, - size_t limit -); +CX_EXPORT CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit); /** * Creates a string tokenization context. @@ -1153,7 +1070,7 @@ CxStrtokCtx cx_strtok_( * @return (@c CxStrtokCtx) a new string tokenization context */ #define cx_strtok(str, delim, limit) \ - cx_strtok_(cx_strcast(str), cx_strcast(delim), (limit)) + cx_strtok_(cx_strcast(str), cx_strcast(delim), (limit)) /** * Returns the next token. @@ -1165,14 +1082,8 @@ CxStrtokCtx cx_strtok_( * @return true if successful, false if the limit or the end of the string * has been reached */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_access_w(2) -cx_attr_export -bool cx_strtok_next( - CxStrtokCtx *ctx, - cxstring *token -); +cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2) +CX_EXPORT bool cx_strtok_next(CxStrtokCtx *ctx, cxstring *token); /** * Returns the next token of a mutable string. @@ -1188,14 +1099,8 @@ bool cx_strtok_next( * @return true if successful, false if the limit or the end of the string * has been reached */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_access_w(2) -cx_attr_export -bool cx_strtok_next_m( - CxStrtokCtx *ctx, - cxmutstr *token -); +cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2) +CX_EXPORT bool cx_strtok_next_m(CxStrtokCtx *ctx, cxmutstr *token); /** * Defines an array of more delimiters for the specified tokenization context. @@ -1204,14 +1109,8 @@ bool cx_strtok_next_m( * @param delim array of more delimiters * @param count number of elements in the array */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -void cx_strtok_delim( - CxStrtokCtx *ctx, - const cxstring *delim, - size_t count -); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t count); /* ------------------------------------------------------------------------- * * string to number conversion functions * @@ -1227,12 +1126,12 @@ void cx_strtok_delim( * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1244,12 +1143,12 @@ int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep); * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1261,12 +1160,12 @@ int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep); * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1278,12 +1177,12 @@ int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep); * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1295,12 +1194,12 @@ int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groups * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1312,12 +1211,12 @@ int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep) * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1329,12 +1228,12 @@ int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1346,12 +1245,12 @@ int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1363,12 +1262,12 @@ int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1380,12 +1279,12 @@ int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *g * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1397,12 +1296,12 @@ int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *grou * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1414,12 +1313,12 @@ int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *gr * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1431,12 +1330,12 @@ int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const ch * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1448,12 +1347,12 @@ int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1465,12 +1364,12 @@ int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groups * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1482,12 +1381,12 @@ int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groups * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1499,15 +1398,15 @@ int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groups * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep); /** - * Converts a string to a single precision floating point number. + * Converts a string to a single precision floating-point number. * * The function returns non-zero when conversion is not possible. * In that case the function sets errno to EINVAL when the reason is an invalid character. @@ -1516,15 +1415,15 @@ int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep); * @param str the string to convert * @param output a pointer to the float variable where the result shall be stored * @param decsep the decimal separator - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep); /** - * Converts a string to a double precision floating point number. + * Converts a string to a double precision floating-point number. * * The function returns non-zero when conversion is not possible. * In that case the function sets errno to EINVAL when the reason is an invalid character. @@ -1533,12 +1432,12 @@ int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep * @param str the string to convert * @param output a pointer to the float variable where the result shall be stored * @param decsep the decimal separator - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export -int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep); /** * Converts a string to a number. @@ -1550,7 +1449,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1566,7 +1465,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1582,7 +1481,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1598,7 +1497,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1614,7 +1513,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1630,7 +1529,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1646,7 +1545,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1662,7 +1561,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1678,7 +1577,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1694,7 +1593,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1710,7 +1609,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1726,7 +1625,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1742,7 +1641,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1758,7 +1657,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1774,7 +1673,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1790,7 +1689,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1806,7 +1705,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 - * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @param groupsep (@c const @c char*) each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ @@ -1819,7 +1718,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1837,7 +1736,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1855,7 +1754,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1873,7 +1772,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1891,7 +1790,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1909,7 +1808,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1927,7 +1826,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1945,7 +1844,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1963,7 +1862,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1981,7 +1880,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -1999,7 +1898,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -2017,7 +1916,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -2035,7 +1934,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -2053,7 +1952,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -2071,7 +1970,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -2089,7 +1988,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -2107,7 +2006,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. * It sets errno to ERANGE when the target datatype is too small. * - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). * * @param str the string to convert @@ -2119,7 +2018,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse #define cx_strtou64(str, output, base) cx_strtou64_lc_(cx_strcast(str), output, base, ",") /** - * Converts a string to a single precision floating point number. + * Converts a string to a single precision floating-point number. * * The function returns non-zero when conversion is not possible. * In that case the function sets errno to EINVAL when the reason is an invalid character. @@ -2128,14 +2027,14 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the float variable where the result shall be stored * @param decsep the decimal separator - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ #define cx_strtof_lc(str, output, decsep, groupsep) cx_strtof_lc_(cx_strcast(str), output, decsep, groupsep) /** - * Converts a string to a double precision floating point number. + * Converts a string to a double precision floating-point number. * * The function returns non-zero when conversion is not possible. * In that case the function sets errno to EINVAL when the reason is an invalid character. @@ -2143,21 +2042,21 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse * @param str the string to convert * @param output a pointer to the double variable where the result shall be stored * @param decsep the decimal separator - * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @param groupsep each character in this string is treated as a group separator and ignored during conversion * @retval zero success * @retval non-zero conversion was not possible */ #define cx_strtod_lc(str, output, decsep, groupsep) cx_strtod_lc_(cx_strcast(str), output, decsep, groupsep) /** - * Converts a string to a single precision floating point number. + * Converts a string to a single precision floating-point number. * * The function returns non-zero when conversion is not possible. * In that case the function sets errno to EINVAL when the reason is an invalid character. * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h. * * The decimal separator is assumed to be a dot character. - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose a different format, use cx_strtof_lc(). * * @param str the string to convert @@ -2168,13 +2067,13 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse #define cx_strtof(str, output) cx_strtof_lc_(cx_strcast(str), output, '.', ",") /** - * Converts a string to a double precision floating point number. + * Converts a string to a double precision floating-point number. * * The function returns non-zero when conversion is not possible. * In that case the function sets errno to EINVAL when the reason is an invalid character. * * The decimal separator is assumed to be a dot character. - * The comma character is treated as group separator and ignored during parsing. + * The comma character is treated as a group separator and ignored during parsing. * If you want to choose a different format, use cx_strtof_lc(). * * @param str the string to convert diff --git a/ucx/cx/test.h b/ucx/cx/test.h index fdd9269..ff86b33 100644 --- a/ucx/cx/test.h +++ b/ucx/cx/test.h @@ -56,7 +56,7 @@ * } * * - * @attention Do not call own functions within a test, that use + * @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 @@ -136,10 +136,7 @@ struct CxTestSuite { * @param name optional name of the suite * @return a new test suite */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_malloc +cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1) cx_attr_malloc static inline CxTestSuite* cx_test_suite_new(const char *name) { CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite)); if (suite != NULL) { @@ -157,7 +154,7 @@ static inline CxTestSuite* cx_test_suite_new(const char *name) { * * @param suite the test suite to free */ -static inline void cx_test_suite_free(CxTestSuite* suite) { +CX_INLINE void cx_test_suite_free(CxTestSuite* suite) { if (suite == NULL) return; CxTestSet *l = suite->tests; while (l != NULL) { @@ -171,13 +168,13 @@ static inline void cx_test_suite_free(CxTestSuite* suite) { /** * Registers a test function with the specified test suite. * - * @param suite the suite, the test function shall be added to + * @param suite the suite the test function shall be added to * @param test the test function to register * @retval zero success * @retval non-zero failure */ cx_attr_nonnull -static inline int cx_test_register(CxTestSuite* suite, CxTest test) { +CX_INLINE int cx_test_register(CxTestSuite* suite, CxTest test) { CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet)); if (t) { t->test = test; @@ -204,8 +201,7 @@ static inline int cx_test_register(CxTestSuite* suite, CxTest test) { * @param out_writer the write function writing to @p out_target */ cx_attr_nonnull -static inline void cx_test_run(CxTestSuite *suite, - void *out_target, cx_write_func out_writer) { +CX_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 { @@ -263,7 +259,7 @@ static inline void cx_test_run(CxTestSuite *suite, * } * @endcode * - * @attention Any CX_TEST_ASSERT() calls must be performed in scope of + * @attention Any CX_TEST_ASSERT() calls must be performed in the scope of * #CX_TEST_DO. */ #define CX_TEST_DO _writefnc_("Running ", 1, 8, _output_);\ diff --git a/ucx/cx/tree.h b/ucx/cx/tree.h index 14832db..a1412ca 100644 --- a/ucx/cx/tree.h +++ b/ucx/cx/tree.h @@ -49,12 +49,12 @@ extern "C" { * * This iterator is not position-aware in a strict sense, as it does not assume * a particular order of elements in the tree. However, the iterator keeps track - * of the number of nodes it has passed in a counter variable. + * of the number of nodes it has passed in a counter-variable. * Each node, regardless of the number of passes, is counted only once. * * @note Objects that are pointed to by an iterator are mutable through that * iterator. However, if the - * underlying data structure is mutated by other means than this iterator (e.g. + * underlying data structure is mutated by other means than this iterator (e.g., * elements added or removed), the iterator becomes invalid (regardless of what * cxIteratorValid() returns). * @@ -71,7 +71,7 @@ typedef struct cx_tree_iterator_s { bool skip; /** * Set to true, when the iterator shall visit a node again - * when all it's children have been processed. + * when all its children have been processed. */ bool visit_on_exit; /** @@ -97,7 +97,7 @@ typedef struct cx_tree_iterator_s { */ void *node; /** - * Stores a copy of the next pointer of the visited node. + * Stores a copy of the pointer to the successor of the visited node. * Allows freeing a node on exit without corrupting the iteration. */ void *node_next; @@ -155,12 +155,12 @@ struct cx_tree_visitor_queue_s { * * This iterator is not position-aware in a strict sense, as it does not assume * a particular order of elements in the tree. However, the iterator keeps track - * of the number of nodes it has passed in a counter variable. + * of the number of nodes it has passed in a counter-variable. * Each node, regardless of the number of passes, is counted only once. * * @note Objects that are pointed to by an iterator are mutable through that * iterator. However, if the - * underlying data structure is mutated by other means than this iterator (e.g. + * underlying data structure is mutated by other means than this iterator (e.g., * elements added or removed), the iterator becomes invalid (regardless of what * cxIteratorValid() returns). * @@ -212,24 +212,14 @@ typedef struct cx_tree_visitor_s { * @param iter the iterator */ cx_attr_nonnull -static inline void cxTreeIteratorDispose(CxTreeIterator *iter) { - cxFreeDefault(iter->stack); - iter->stack = NULL; -} +CX_EXPORT void cxTreeIteratorDispose(CxTreeIterator *iter); /** * Releases internal memory of the given tree visitor. * @param visitor the visitor */ cx_attr_nonnull -static inline void cxTreeVisitorDispose(CxTreeVisitor *visitor) { - struct cx_tree_visitor_queue_s *q = visitor->queue_next; - while (q != NULL) { - struct cx_tree_visitor_queue_s *next = q->next; - cxFreeDefault(q); - q = next; - } -} +CX_EXPORT void cxTreeVisitorDispose(CxTreeVisitor *visitor); /** * Advises the iterator to skip the subtree below the current node and @@ -250,7 +240,7 @@ static inline void cxTreeVisitorDispose(CxTreeVisitor *visitor) { /** * Links a node to a (new) parent. * - * If the node has already a parent, it is unlinked, first. + * If the node already has a parent, it is unlinked, first. * If the parent has children already, the node is @em appended to the list * of all currently existing children. * @@ -265,16 +255,9 @@ static inline void cxTreeVisitorDispose(CxTreeVisitor *visitor) { * @see cx_tree_unlink() */ cx_attr_nonnull -cx_attr_export -void cx_tree_link( - void *parent, - void *node, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_tree_link(void *parent, void *node, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Unlinks a node from its parent. @@ -291,15 +274,9 @@ void cx_tree_link( * @see cx_tree_link() */ cx_attr_nonnull -cx_attr_export -void cx_tree_unlink( - void *node, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_tree_unlink(void *node, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Macro that can be used instead of the magic value for infinite search depth. @@ -318,8 +295,8 @@ void cx_tree_unlink( * Zero means exact match and a positive number is an implementation defined * measure for the distance to an exact match. * - * For example if a tree stores file path information, a node that is - * describing a parent directory of a filename that is searched, shall + * For example, consider a tree that stores file path information. + * A node which is describing a parent directory of a searched file shall * return a positive number to indicate that a child node might contain the * searched item. On the other hand, if the node denotes a path that is not a * prefix of the searched filename, the function would return -1 to indicate @@ -330,9 +307,8 @@ void cx_tree_unlink( * * @return 0 if the node contains the data, * positive if one of the children might contain the data, - * negative if neither the node, nor the children contains the data + * negative if neither the node nor the children contains the data */ -cx_attr_nonnull typedef int (*cx_tree_search_data_func)(const void *node, const void *data); @@ -348,8 +324,8 @@ typedef int (*cx_tree_search_data_func)(const void *node, const void *data); * Zero means exact match and a positive number is an implementation defined * measure for the distance to an exact match. * - * For example if a tree stores file path information, a node that is - * describing a parent directory of a filename that is searched, shall + * For example, consider a tree that stores file path information. + * A node which is describing a parent directory of a searched file shall * return a positive number to indicate that a child node might contain the * searched item. On the other hand, if the node denotes a path that is not a * prefix of the searched filename, the function would return -1 to indicate @@ -360,19 +336,18 @@ typedef int (*cx_tree_search_data_func)(const void *node, const void *data); * * @return 0 if @p node contains the same data as @p new_node, * positive if one of the children might contain the data, - * negative if neither the node, nor the children contains the data + * negative if neither the node nor the children contains the data */ -cx_attr_nonnull typedef int (*cx_tree_search_func)(const void *node, const void *new_node); /** * Searches for data in a tree. * - * When the data cannot be found exactly, the search function might return a - * closest result which might be a good starting point for adding a new node + * When the data cannot be found exactly, the search function might return the + * closest result, which might be a good starting point for adding a new node * to the tree (see also #cx_tree_add()). * - * Depending on the tree structure it is not necessarily guaranteed that the + * Depending on the tree structure, it is not necessarily guaranteed that the * "closest" match is uniquely defined. This function will search for a node * with the best match according to the @p sfunc (meaning: the return value of * @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary @@ -389,27 +364,19 @@ typedef int (*cx_tree_search_func)(const void *node, const void *new_node); * could contain the node (but doesn't right now), negative if the tree does not * contain any node that might be related to the searched data */ -cx_attr_nonnull -cx_attr_access_w(5) -cx_attr_export -int cx_tree_search_data( - const void *root, - size_t depth, - const void *data, - cx_tree_search_data_func sfunc, - void **result, - ptrdiff_t loc_children, - ptrdiff_t loc_next -); +cx_attr_nonnull cx_attr_access_w(5) +CX_EXPORT int cx_tree_search_data(const void *root, size_t depth, + const void *data, cx_tree_search_data_func sfunc, + void **result, ptrdiff_t loc_children, ptrdiff_t loc_next); /** * Searches for a node in a tree. * * When no node with the same data can be found, the search function might - * return a closest result which might be a good starting point for adding the + * return the closest result, which might be a good starting point for adding the * new node to the tree (see also #cx_tree_add()). * - * Depending on the tree structure it is not necessarily guaranteed that the + * Depending on the tree structure, it is not necessarily guaranteed that the * "closest" match is uniquely defined. This function will search for a node * with the best match according to the @p sfunc (meaning: the return value of * @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary @@ -426,18 +393,10 @@ int cx_tree_search_data( * could contain the node (but doesn't right now), negative if the tree does not * contain any node that might be related to the searched data */ -cx_attr_nonnull -cx_attr_access_w(5) -cx_attr_export -int cx_tree_search( - const void *root, - size_t depth, - const void *node, - cx_tree_search_func sfunc, - void **result, - ptrdiff_t loc_children, - ptrdiff_t loc_next -); +cx_attr_nonnull cx_attr_access_w(5) +CX_EXPORT int cx_tree_search(const void *root, size_t depth, + const void *node, cx_tree_search_func sfunc, + void **result, ptrdiff_t loc_children, ptrdiff_t loc_next); /** * Creates a depth-first iterator for a tree with the specified root node. @@ -460,13 +419,8 @@ int cx_tree_search( * @see cxTreeIteratorDispose() */ cx_attr_nodiscard -cx_attr_export -CxTreeIterator cx_tree_iterator( - void *root, - bool visit_on_exit, - ptrdiff_t loc_children, - ptrdiff_t loc_next -); +CX_EXPORT CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit, + ptrdiff_t loc_children, ptrdiff_t loc_next); /** * Creates a breadth-first iterator for a tree with the specified root node. @@ -487,24 +441,19 @@ CxTreeIterator cx_tree_iterator( * @see cxTreeVisitorDispose() */ cx_attr_nodiscard -cx_attr_export -CxTreeVisitor cx_tree_visitor( - void *root, - ptrdiff_t loc_children, - ptrdiff_t loc_next -); +CX_EXPORT CxTreeVisitor cx_tree_visitor(void *root, + ptrdiff_t loc_children, ptrdiff_t loc_next); /** * Describes a function that creates a tree node from the specified data. - * The first argument points to the data the node shall contain and - * the second argument may be used for additional data (e.g. an allocator). + * The first argument points to the data the node shall contain, and + * the second argument may be used for additional data (e.g., an allocator). * Functions of this type shall either return a new pointer to a newly * created node or @c NULL when allocation fails. * * @note the function may leave the node pointers in the struct uninitialized. * The caller is responsible to set them according to the intended use case. */ -cx_attr_nonnull_arg(1) typedef void *(*cx_tree_node_create_func)(const void *, void *); /** @@ -513,8 +462,7 @@ typedef void *(*cx_tree_node_create_func)(const void *, void *); * This variable is used by #cx_tree_add_array() and #cx_tree_add_iter() to * implement optimized insertion of multiple elements into a tree. */ -cx_attr_export -extern unsigned int cx_tree_add_look_around_depth; +CX_EXPORT extern unsigned int cx_tree_add_look_around_depth; /** * Adds multiple elements efficiently to a tree. @@ -554,23 +502,12 @@ extern unsigned int cx_tree_add_look_around_depth; * @return the number of nodes created and added * @see cx_tree_add() */ -cx_attr_nonnull_arg(1, 3, 4, 6, 7) -cx_attr_access_w(6) -cx_attr_export -size_t cx_tree_add_iter( - struct cx_iterator_base_s *iter, - size_t num, - cx_tree_search_func sfunc, - cx_tree_node_create_func cfunc, - void *cdata, - void **failed, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(1, 3, 4, 6, 7) cx_attr_access_w(6) +CX_EXPORT size_t cx_tree_add_iter(struct cx_iterator_base_s *iter, size_t num, + cx_tree_search_func sfunc, cx_tree_node_create_func cfunc, + void *cdata, void **failed, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Adds multiple elements efficiently to a tree. @@ -609,24 +546,12 @@ size_t cx_tree_add_iter( * @return the number of array elements successfully processed * @see cx_tree_add() */ -cx_attr_nonnull_arg(1, 4, 5, 7, 8) -cx_attr_access_w(7) -cx_attr_export -size_t cx_tree_add_array( - const void *src, - size_t num, - size_t elem_size, - cx_tree_search_func sfunc, - cx_tree_node_create_func cfunc, - void *cdata, - void **failed, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(1, 4, 5, 7, 8) cx_attr_access_w(7) +CX_EXPORT size_t cx_tree_add_array(const void *src, size_t num, size_t elem_size, + cx_tree_search_func sfunc, cx_tree_node_create_func cfunc, + void *cdata, void **failed, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Adds data to a tree. @@ -637,17 +562,17 @@ size_t cx_tree_add_array( * When a location is found, the @p cfunc will be invoked with @p cdata. * * The node returned by @p cfunc will be linked into the tree. - * When @p sfunc returned a positive integer, the new node will be linked as a + * When @p sfunc returns a positive integer, the new node will be linked as a * child. The other children (now siblings of the new node) are then checked * with @p sfunc, whether they could be children of the new node and re-linked * accordingly. * - * When @p sfunc returned zero and the found node has a parent, the new - * node will be added as sibling - otherwise, the new node will be added + * When @p sfunc returns zero and the found node has a parent, the new + * node will be added as a sibling - otherwise, the new node will be added * as a child. * - * When @p sfunc returned a negative value, the new node will not be added to - * the tree and this function returns a non-zero value. + * When @p sfunc returns a negative value, the new node will not be added to + * the tree, and this function returns a non-zero value. * The caller should check if @p cnode contains a node pointer and deal with the * node that could not be added. * @@ -673,22 +598,12 @@ size_t cx_tree_add_array( * @return zero when a new node was created and added to the tree, * non-zero otherwise */ -cx_attr_nonnull_arg(1, 2, 3, 5, 6) -cx_attr_access_w(5) -cx_attr_export -int cx_tree_add( - const void *src, - cx_tree_search_func sfunc, - cx_tree_node_create_func cfunc, - void *cdata, - void **cnode, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(1, 2, 3, 5, 6) cx_attr_access_w(5) +CX_EXPORT int cx_tree_add(const void *src, + cx_tree_search_func sfunc, cx_tree_node_create_func cfunc, + void *cdata, void **cnode, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** @@ -747,9 +662,9 @@ struct cx_tree_s { * A function to create new nodes. * * Invocations to this function will receive a pointer to this tree - * structure as second argument. + * structure as the second argument. * - * Nodes MAY use #cx_tree_node_base_s as base layout, but do not need to. + * Nodes MAY use #cx_tree_node_base_s as the base layout, but do not need to. */ cx_tree_node_create_func node_create; @@ -814,7 +729,7 @@ struct cx_tree_s { * Macro to roll out the #cx_tree_node_base_s structure with a custom * node type. * - * Must be used as first member in your custom tree struct. + * Must be used as the first member in your custom tree struct. * * @param type the data type for the nodes */ @@ -850,32 +765,20 @@ struct cx_tree_class_s { * Implementations SHALL NOT simply invoke @p insert_many as this comes * with too much overhead. */ - int (*insert_element)( - struct cx_tree_s *tree, - const void *data - ); + int (*insert_element)(struct cx_tree_s *tree, const void *data); /** * Member function for inserting multiple elements. * - * Implementations SHALL avoid to perform a full search in the tree for + * Implementations SHALL avoid performing a full search in the tree for * every element even though the source data MAY be unsorted. */ - size_t (*insert_many)( - struct cx_tree_s *tree, - struct cx_iterator_base_s *iter, - size_t n - ); + size_t (*insert_many)(struct cx_tree_s *tree, struct cx_iterator_base_s *iter, size_t n); /** * Member function for finding a node. */ - void *(*find)( - struct cx_tree_s *tree, - const void *subtree, - const void *data, - size_t depth - ); + void *(*find)(struct cx_tree_s *tree, const void *subtree, const void *data, size_t depth); }; /** @@ -885,7 +788,7 @@ typedef struct cx_tree_s CxTree; /** - * Destroys a node and it's subtree. + * Destroys a node and its subtree. * * It is guaranteed that the simple destructor is invoked before * the advanced destructor, starting with the leaf nodes of the subtree. @@ -906,8 +809,7 @@ typedef struct cx_tree_s CxTree; * @see cxTreeFree() */ cx_attr_nonnull -cx_attr_export -void cxTreeDestroySubtree(CxTree *tree, void *node); +CX_EXPORT void cxTreeDestroySubtree(CxTree *tree, void *node); /** @@ -921,7 +823,7 @@ void cxTreeDestroySubtree(CxTree *tree, void *node); * * @attention Be careful when calling this function when no destructor function * is registered that actually frees the memory of nodes. In that case you will - * need a reference to the (former) root node of the tree somewhere or + * need a reference to the (former) root node of the tree somewhere, or * otherwise you will be leaking memory. * * @param tree the tree @@ -945,8 +847,7 @@ void cxTreeDestroySubtree(CxTree *tree, void *node); * * @param tree the tree to free */ -cx_attr_export -void cxTreeFree(CxTree *tree); +CX_EXPORT void cxTreeFree(CxTree *tree); /** * Creates a new tree structure based on the specified layout. @@ -972,29 +873,18 @@ void cxTreeFree(CxTree *tree); * @see cxTreeCreateSimple() * @see cxTreeCreateWrapped() */ -cx_attr_nonnull_arg(2, 3, 4) -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxTreeFree, 1) -cx_attr_export -CxTree *cxTreeCreate( - const CxAllocator *allocator, - cx_tree_node_create_func create_func, - cx_tree_search_func search_func, - cx_tree_search_data_func search_data_func, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(2, 3, 4) cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1) +CX_EXPORT CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func, + cx_tree_search_func search_func, cx_tree_search_data_func search_data_func, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Creates a new tree structure based on a default layout. * - * Nodes created by @p create_func MUST contain #cx_tree_node_base_s as first + * Nodes created by @p create_func MUST contain #cx_tree_node_base_s as the first * member (or at least respect the default offsets specified in the tree - * struct) and they MUST be allocated with the specified allocator. + * struct), and they MUST be allocated with the specified allocator. * * @note This function will also register an advanced destructor which * will free the nodes with the allocator's free() method. @@ -1006,10 +896,8 @@ CxTree *cxTreeCreate( * @return (@c CxTree*) the new tree * @see cxTreeCreate() */ -#define cxTreeCreateSimple(\ - allocator, create_func, search_func, search_data_func \ -) cxTreeCreate(allocator, create_func, search_func, search_data_func, \ -cx_tree_node_base_layout) +#define cxTreeCreateSimple(allocator, create_func, search_func, search_data_func) \ + cxTreeCreate(allocator, create_func, search_func, search_data_func, cx_tree_node_base_layout) /** * Creates a new tree structure based on an existing tree. @@ -1019,7 +907,7 @@ cx_tree_node_base_layout) * @attention This function will create an incompletely defined tree structure * where neither the create function, the search function, nor a destructor * will be set. If you wish to use any of this functionality for the wrapped - * tree, you need to specify those functions afterwards. + * tree, you need to specify those functions afterward. * * @param allocator the allocator that was used for nodes of the wrapped tree * (if @c NULL, the cxDefaultAllocator is assumed) @@ -1033,20 +921,10 @@ cx_tree_node_base_layout) * @return the new tree * @see cxTreeCreate() */ -cx_attr_nonnull_arg(2) -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxTreeFree, 1) -cx_attr_export -CxTree *cxTreeCreateWrapped( - const CxAllocator *allocator, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(2) cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1) +CX_EXPORT CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Inserts data into the tree. @@ -1061,12 +939,7 @@ CxTree *cxTreeCreateWrapped( * @retval non-zero failure */ cx_attr_nonnull -static inline int cxTreeInsert( - CxTree *tree, - const void *data -) { - return tree->cl->insert_element(tree, data); -} +CX_EXPORT int cxTreeInsert(CxTree *tree, const void *data); /** * Inserts elements provided by an iterator efficiently into the tree. @@ -1081,13 +954,7 @@ static inline int cxTreeInsert( * @return the number of elements that could be successfully inserted */ cx_attr_nonnull -static inline size_t cxTreeInsertIter( - CxTree *tree, - CxIteratorBase *iter, - size_t n -) { - return tree->cl->insert_many(tree, iter, n); -} +CX_EXPORT size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n); /** * Inserts an array of data efficiently into the tree. @@ -1103,17 +970,7 @@ static inline size_t cxTreeInsertIter( * @return the number of elements that could be successfully inserted */ cx_attr_nonnull -static inline size_t cxTreeInsertArray( - CxTree *tree, - const void *data, - size_t elem_size, - size_t n -) { - if (n == 0) return 0; - if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0; - CxIterator iter = cxIterator(data, elem_size, n); - return cxTreeInsertIter(tree, cxIteratorRef(iter), n); -} +CX_EXPORT size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n); /** * Searches the data in the specified tree. @@ -1126,14 +983,8 @@ static inline size_t cxTreeInsertArray( * @param data the data to search for * @return the first matching node, or @c NULL when the data cannot be found */ -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cxTreeFind( - CxTree *tree, - const void *data -) { - return tree->cl->find(tree, tree->root, data, 0); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT void *cxTreeFind(CxTree *tree, const void *data); /** * Searches the data in the specified subtree. @@ -1154,16 +1005,8 @@ static inline void *cxTreeFind( * @param max_depth the maximum search depth * @return the first matching node, or @c NULL when the data cannot be found */ -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cxTreeFindInSubtree( - CxTree *tree, - const void *data, - void *subtree_root, - size_t max_depth -) { - return tree->cl->find(tree, subtree_root, data, max_depth); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth); /** * Determines the size of the specified subtree. @@ -1172,10 +1015,8 @@ static inline void *cxTreeFindInSubtree( * @param subtree_root the root node of the subtree * @return the number of nodes in the specified subtree */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root); /** * Determines the depth of the specified subtree. @@ -1184,10 +1025,8 @@ size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root); * @param subtree_root the root node of the subtree * @return the tree depth including the @p subtree_root */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root); /** * Determines the size of the entire tree. @@ -1195,11 +1034,8 @@ size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root); * @param tree the tree * @return the tree size, counting the root as one */ -cx_attr_nonnull -cx_attr_nodiscard -static inline size_t cxTreeSize(CxTree *tree) { - return tree->size; -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxTreeSize(CxTree *tree); /** * Determines the depth of the entire tree. @@ -1207,10 +1043,8 @@ static inline size_t cxTreeSize(CxTree *tree) { * @param tree the tree * @return the tree depth, counting the root as one */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -size_t cxTreeDepth(CxTree *tree); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxTreeDepth(CxTree *tree); /** * Creates a depth-first iterator for the specified tree starting in @p node. @@ -1224,18 +1058,8 @@ size_t cxTreeDepth(CxTree *tree); * @return a tree iterator (depth-first) * @see cxTreeVisit() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline CxTreeIterator cxTreeIterateSubtree( - CxTree *tree, - void *node, - bool visit_on_exit -) { - return cx_tree_iterator( - node, visit_on_exit, - tree->loc_children, tree->loc_next - ); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit); /** * Creates a breadth-first iterator for the specified tree starting in @p node. @@ -1247,13 +1071,8 @@ static inline CxTreeIterator cxTreeIterateSubtree( * @return a tree visitor (a.k.a. breadth-first iterator) * @see cxTreeIterate() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) { - return cx_tree_visitor( - node, tree->loc_children, tree->loc_next - ); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node); /** * Creates a depth-first iterator for the specified tree. @@ -1264,14 +1083,8 @@ static inline CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) { * @return a tree iterator (depth-first) * @see cxTreeVisit() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline CxTreeIterator cxTreeIterate( - CxTree *tree, - bool visit_on_exit -) { - return cxTreeIterateSubtree(tree, tree->root, visit_on_exit); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit); /** * Creates a breadth-first iterator for the specified tree. @@ -1280,16 +1093,13 @@ static inline CxTreeIterator cxTreeIterate( * @return a tree visitor (a.k.a. breadth-first iterator) * @see cxTreeIterate() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline CxTreeVisitor cxTreeVisit(CxTree *tree) { - return cxTreeVisitSubtree(tree, tree->root); -} +cx_attr_nonnull cx_attr_nodiscard +CxTreeVisitor cxTreeVisit(CxTree *tree); /** * Sets the (new) parent of the specified child. * - * If the @p child is not already member of the tree, this function behaves + * If the @p child is not already a member of the tree, this function behaves * as #cxTreeAddChildNode(). * * @param tree the tree @@ -1298,21 +1108,16 @@ static inline CxTreeVisitor cxTreeVisit(CxTree *tree) { * @see cxTreeAddChildNode() */ cx_attr_nonnull -cx_attr_export -void cxTreeSetParent( - CxTree *tree, - void *parent, - void *child -); +CX_EXPORT void cxTreeSetParent(CxTree *tree, void *parent, void *child); /** * Adds a new node to the tree. * - * If the @p child is already member of the tree, the behavior is undefined. + * If the @p child is already a member of the tree, the behavior is undefined. * Use #cxTreeSetParent() if you want to move a subtree to another location. * * @attention The node may be externally created, but MUST obey the same rules - * as if it was created by the tree itself with #cxTreeAddChild() (e.g. use + * as if it was created by the tree itself with #cxTreeAddChild() (e.g., use * the same allocator). * * @param tree the tree @@ -1321,12 +1126,7 @@ void cxTreeSetParent( * @see cxTreeSetParent() */ cx_attr_nonnull -cx_attr_export -void cxTreeAddChildNode( - CxTree *tree, - void *parent, - void *child -); +CX_EXPORT void cxTreeAddChildNode(CxTree *tree, void *parent, void *child); /** * Creates a new node and adds it to the tree. @@ -1336,7 +1136,7 @@ void cxTreeAddChildNode( * leaving this task to the tree by using #cxTreeInsert(). * * Be aware that adding nodes at arbitrary locations in the tree might cause - * wrong or undesired results when subsequently invoking #cxTreeInsert() and + * wrong or undesired results when subsequently invoking #cxTreeInsert(), and * the invariant imposed by the search function does not hold any longer. * * @param tree the tree @@ -1346,12 +1146,7 @@ void cxTreeAddChildNode( * @see cxTreeInsert() */ cx_attr_nonnull -cx_attr_export -int cxTreeAddChild( - CxTree *tree, - void *parent, - const void *data -); +CX_EXPORT int cxTreeAddChild(CxTree *tree, void *parent, const void *data); /** * A function that is invoked when a node needs to be re-linked to a new parent. @@ -1365,7 +1160,6 @@ int cxTreeAddChild( * @param old_parent the old parent of the node * @param new_parent the new parent of the node */ -cx_attr_nonnull typedef void (*cx_tree_relink_func)( void *node, const void *old_parent, @@ -1387,15 +1181,10 @@ typedef void (*cx_tree_relink_func)( * @return zero on success, non-zero if @p node is the root node of the tree */ cx_attr_nonnull_arg(1, 2) -cx_attr_export -int cxTreeRemoveNode( - CxTree *tree, - void *node, - cx_tree_relink_func relink_func -); +CX_EXPORT int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func); /** - * Removes a node and it's subtree from the tree. + * Removes a node and its subtree from the tree. * * If the node is not part of the tree, the behavior is undefined. * @@ -1406,8 +1195,7 @@ int cxTreeRemoveNode( * @param node the node to remove */ cx_attr_nonnull -cx_attr_export -void cxTreeRemoveSubtree(CxTree *tree, void *node); +CX_EXPORT void cxTreeRemoveSubtree(CxTree *tree, void *node); /** * Destroys a node and re-links its children to its former parent. @@ -1428,12 +1216,7 @@ void cxTreeRemoveSubtree(CxTree *tree, void *node); * @return zero on success, non-zero if @p node is the root node of the tree */ cx_attr_nonnull_arg(1, 2) -cx_attr_export -int cxTreeDestroyNode( - CxTree *tree, - void *node, - cx_tree_relink_func relink_func -); +CX_EXPORT int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func); #ifdef __cplusplus } // extern "C" diff --git a/ucx/hash_key.c b/ucx/hash_key.c index fac29bb..25f8c4b 100644 --- a/ucx/hash_key.c +++ b/ucx/hash_key.c @@ -27,6 +27,7 @@ */ #include "cx/hash_key.h" +#include "cx/compare.h" #include void cx_hash_murmur(CxHashKey *key) { @@ -62,14 +63,14 @@ void cx_hash_murmur(CxHashKey *key) { switch (len) { case 3: h ^= (data[i + 2] & 0xFF) << 16; - __attribute__((__fallthrough__)); + cx_attr_fallthrough; case 2: h ^= (data[i + 1] & 0xFF) << 8; - __attribute__((__fallthrough__)); + cx_attr_fallthrough; case 1: h ^= (data[i + 0] & 0xFF); h *= m; - __attribute__((__fallthrough__)); + cx_attr_fallthrough; default: // do nothing ; } @@ -81,6 +82,21 @@ void cx_hash_murmur(CxHashKey *key) { key->hash = h; } + +uint32_t cx_hash_u32(uint32_t x) { + x = ((x >> 16) ^ x) * 0x45d9f3bu; + x = ((x >> 16) ^ x) * 0x45d9f3bu; + x = (x >> 16) ^ x; + return x; +} + +uint64_t cx_hash_u64(uint64_t x) { + x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); + x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); + x = x ^ (x >> 31); + return x; +} + CxHashKey cx_hash_key_str(const char *str) { CxHashKey key; key.data = str; @@ -89,6 +105,22 @@ CxHashKey cx_hash_key_str(const char *str) { return key; } +CxHashKey cx_hash_key_ustr(unsigned const char *str) { + CxHashKey key; + key.data = str; + key.len = str == NULL ? 0 : strlen((const char*)str); + cx_hash_murmur(&key); + return key; +} + +CxHashKey cx_hash_key_cxstr(cxstring str) { + return cx_hash_key(str.ptr, str.length); +} + +CxHashKey cx_hash_key_mutstr(cxmutstr str) { + return cx_hash_key(str.ptr, str.length); +} + CxHashKey cx_hash_key_bytes( const unsigned char *bytes, size_t len @@ -110,3 +142,29 @@ CxHashKey cx_hash_key( cx_hash_murmur(&key); return key; } + +CxHashKey cx_hash_key_u32(uint32_t x) { + CxHashKey key; + key.data = NULL; + key.len = 0; + key.hash = cx_hash_u32(x); + return key; +} + +CxHashKey cx_hash_key_u64(uint64_t x) { + CxHashKey key; + key.data = NULL; + key.len = 0; + key.hash = cx_hash_u64(x); + return key; +} + +int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right) { + int d; + d = cx_vcmp_uint64(left->hash, right->hash); + if (d != 0) return d; + d = cx_vcmp_size(left->len, right->len); + if (d != 0) return d; + if (left->len == 0) return 0; + return memcmp(left->data, right->data, left->len); +} diff --git a/ucx/hash_map.c b/ucx/hash_map.c index 1721e8c..82ae21e 100644 --- a/ucx/hash_map.c +++ b/ucx/hash_map.c @@ -78,7 +78,7 @@ static void cx_hash_map_destructor(struct cx_map_s *map) { cxFree(map->collection.allocator, map); } -static int cx_hash_map_put( +static void *cx_hash_map_put( CxMap *map, CxHashKey key, void *value @@ -101,11 +101,12 @@ static int cx_hash_map_put( elm = elm->next; } - if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len && - memcmp(elm->key.data, key.data, key.len) == 0) { + if (elm != NULL && cx_hash_key_cmp(&elm->key, &key) == 0) { // overwrite existing element, but call destructors first cx_invoke_destructor(map, elm->data); - if (map->collection.store_pointer) { + if (value == NULL) { + memset(elm->data, 0, map->collection.elem_size); + } else if (map->collection.store_pointer) { memcpy(elm->data, &value, sizeof(void *)); } else { memcpy(elm->data, value, map->collection.elem_size); @@ -116,10 +117,12 @@ static int cx_hash_map_put( allocator, sizeof(struct cx_hash_map_element_s) + map->collection.elem_size ); - if (e == NULL) return -1; + if (e == NULL) return NULL; // write the value - if (map->collection.store_pointer) { + if (value == NULL) { + memset(e->data, 0, map->collection.elem_size); + } else if (map->collection.store_pointer) { memcpy(e->data, &value, sizeof(void *)); } else { memcpy(e->data, value, map->collection.elem_size); @@ -127,7 +130,10 @@ static int cx_hash_map_put( // copy the key void *kd = cxMalloc(allocator, key.len); - if (kd == NULL) return -1; + if (kd == NULL) { + cxFree(allocator, e); + return NULL; + } memcpy(kd, key.data, key.len); e->key.data = kd; e->key.len = key.len; @@ -140,12 +146,14 @@ static int cx_hash_map_put( prev->next = e; } e->next = elm; + elm = e; // increase the size map->collection.size++; } - return 0; + // return pointer to the element + return elm->data; } static void cx_hash_map_unlink( @@ -205,27 +213,25 @@ static int cx_hash_map_get_remove( struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; struct cx_hash_map_element_s *prev = NULL; while (elm && elm->key.hash <= hash) { - if (elm->key.hash == hash && elm->key.len == key.len) { - if (memcmp(elm->key.data, key.data, key.len) == 0) { - if (remove) { - if (targetbuf == NULL) { - cx_invoke_destructor(map, elm->data); - } else { - memcpy(targetbuf, elm->data, map->collection.elem_size); - } - cx_hash_map_unlink(hash_map, slot, prev, elm); + if (cx_hash_key_cmp(&elm->key, &key) == 0) { + if (remove) { + if (targetbuf == NULL) { + cx_invoke_destructor(map, elm->data); + } else { + memcpy(targetbuf, elm->data, map->collection.elem_size); + } + cx_hash_map_unlink(hash_map, slot, prev, elm); + } else { + assert(targetbuf != NULL); + void *data = NULL; + if (map->collection.store_pointer) { + data = *(void **) elm->data; } else { - assert(targetbuf != NULL); - void *data = NULL; - if (map->collection.store_pointer) { - data = *(void **) elm->data; - } else { - data = elm->data; - } - memcpy(targetbuf, &data, sizeof(void *)); + data = elm->data; } - return 0; + memcpy(targetbuf, &data, sizeof(void *)); } + return 0; } prev = elm; elm = prev->next; @@ -260,19 +266,12 @@ static void *cx_hash_map_iter_current_entry(const void *it) { static void *cx_hash_map_iter_current_key(const void *it) { const CxMapIterator *iter = it; - struct cx_hash_map_element_s *elm = iter->elem; - return &elm->key; + return (void*) iter->entry.key; } static void *cx_hash_map_iter_current_value(const void *it) { const CxMapIterator *iter = it; - const CxMap *map = iter->map.c; - struct cx_hash_map_element_s *elm = iter->elem; - if (map->collection.store_pointer) { - return *(void **) elm->data; - } else { - return elm->data; - } + return iter->entry.value; } static bool cx_hash_map_iter_valid(const void *it) { @@ -282,7 +281,7 @@ static bool cx_hash_map_iter_valid(const void *it) { static void cx_hash_map_iter_next(void *it) { CxMapIterator *iter = it; - CxMap *map = iter->map.m; + CxMap *map = iter->map; struct cx_hash_map_s *hmap = (struct cx_hash_map_s *) map; struct cx_hash_map_element_s *elm = iter->elem; @@ -309,6 +308,7 @@ static void cx_hash_map_iter_next(void *it) { // unlink cx_hash_map_unlink(hmap, iter->slot, prev, elm); + iter->elem_count--; // advance elm = next; @@ -329,7 +329,7 @@ static void cx_hash_map_iter_next(void *it) { // must not modify the iterator (the parameter is const) if (elm != NULL) { iter->entry.key = &elm->key; - if (iter->map.c->collection.store_pointer) { + if (map->collection.store_pointer) { iter->entry.value = *(void **) elm->data; } else { iter->entry.value = elm->data; @@ -343,7 +343,7 @@ static CxMapIterator cx_hash_map_iterator( ) { CxMapIterator iter; - iter.map.c = map; + iter.map = (CxMap*) map; iter.elem_count = map->collection.size; switch (type) { @@ -366,7 +366,7 @@ static CxMapIterator cx_hash_map_iterator( iter.base.valid = cx_hash_map_iter_valid; iter.base.next = cx_hash_map_iter_next; iter.base.remove = false; - iter.base.mutating = false; + iter.base.allow_remove = true; iter.slot = 0; iter.index = 0; diff --git a/ucx/iterator.c b/ucx/iterator.c index 6a78a69..292c11e 100644 --- a/ucx/iterator.c +++ b/ucx/iterator.c @@ -53,7 +53,7 @@ static void cx_iter_next_fast(void *it) { // only move the last element when we are not currently aiming // at the last element already if (iter->index < iter->elem_count) { - void *last = ((char *) iter->src_handle.m) + void *last = ((char *) iter->src_handle) + iter->elem_count * iter->elem_size; memcpy(iter->elem_handle, last, iter->elem_size); } @@ -84,8 +84,8 @@ static void cx_iter_next_slow(void *it) { } } -CxIterator cxMutIterator( - void *array, +CxIterator cxIterator( + const void *array, size_t elem_size, size_t elem_count, bool remove_keeps_order @@ -93,44 +93,25 @@ CxIterator cxMutIterator( CxIterator iter; iter.index = 0; - iter.src_handle.m = array; - iter.elem_handle = array; + iter.src_handle = (void*) array; + iter.elem_handle = (void*) array; iter.elem_size = elem_size; iter.elem_count = array == NULL ? 0 : elem_count; iter.base.valid = cx_iter_valid; iter.base.current = cx_iter_current; iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast; iter.base.remove = false; - iter.base.mutating = true; + iter.base.allow_remove = true; return iter; } -CxIterator cxIterator( +CxIterator cxIteratorPtr( const void *array, - size_t elem_size, - size_t elem_count -) { - CxIterator iter = cxMutIterator((void*)array, elem_size, elem_count, false); - iter.base.mutating = false; - return iter; -} - -CxIterator cxMutIteratorPtr( - void *array, size_t elem_count, bool remove_keeps_order ) { - CxIterator iter = cxMutIterator(array, sizeof(void*), elem_count, remove_keeps_order); + CxIterator iter = cxIterator(array, sizeof(void*), elem_count, remove_keeps_order); iter.base.current = cx_iter_current_ptr; return iter; } - -CxIterator cxIteratorPtr( - const void *array, - size_t elem_count -) { - CxIterator iter = cxMutIteratorPtr((void*) array, elem_count, false); - iter.base.mutating = false; - return iter; -} diff --git a/ucx/json.c b/ucx/json.c index 484c7d3..caf1c72 100644 --- a/ucx/json.c +++ b/ucx/json.c @@ -32,6 +32,7 @@ #include #include #include +#include /* * RFC 8259 @@ -46,22 +47,17 @@ static int json_cmp_objvalue(const void *l, const void *r) { return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name)); } -static CxJsonObjValue *json_find_objvalue(const CxJsonValue *obj, cxstring name) { +static size_t json_find_objvalue(const CxJsonValue *obj, cxstring name) { assert(obj->type == CX_JSON_OBJECT); CxJsonObjValue kv_dummy; kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length); - size_t index = cx_array_binary_search( + return cx_array_binary_search( obj->value.object.values, obj->value.object.values_size, sizeof(CxJsonObjValue), &kv_dummy, json_cmp_objvalue ); - if (index == obj->value.object.values_size) { - return NULL; - } else { - return &obj->value.object.values[index]; - } } static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) { @@ -132,16 +128,6 @@ static void token_destroy(CxJsonToken *token) { } } -static bool json_isdigit(char c) { - // TODO: remove once UCX has public API for this - return c >= '0' && c <= '9'; -} - -static bool json_isspace(char c) { - // TODO: remove once UCX has public API for this - return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f'; -} - static int num_isexp(const char *content, size_t length, size_t pos) { if (pos >= length) { return 0; @@ -150,7 +136,7 @@ static int num_isexp(const char *content, size_t length, size_t pos) { int ok = 0; for (size_t i = pos; i < length; i++) { char c = content[i]; - if (json_isdigit(c)) { + if (isdigit((unsigned char)c)) { ok = 1; } else if (i == pos) { if (!(c == '+' || c == '-')) { @@ -167,7 +153,7 @@ static int num_isexp(const char *content, size_t length, size_t pos) { static CxJsonTokenType token_numbertype(const char *content, size_t length) { if (length == 0) return CX_JSON_TOKEN_ERROR; - if (content[0] != '-' && !json_isdigit(content[0])) { + if (content[0] != '-' && !isdigit((unsigned char)content[0])) { return CX_JSON_TOKEN_ERROR; } @@ -180,7 +166,7 @@ static CxJsonTokenType token_numbertype(const char *content, size_t length) { type = CX_JSON_TOKEN_NUMBER; } else if (content[i] == 'e' || content[i] == 'E') { return num_isexp(content, length, i + 1) ? CX_JSON_TOKEN_NUMBER : CX_JSON_TOKEN_ERROR; - } else if (!json_isdigit(content[i])) { + } else if (!isdigit((unsigned char)content[i])) { return CX_JSON_TOKEN_ERROR; // char is not a digit, decimal separator or exponent sep } } @@ -244,7 +230,7 @@ static CxJsonTokenType char2ttype(char c) { return CX_JSON_TOKEN_STRING; } default: { - if (json_isspace(c)) { + if (isspace((unsigned char)c)) { return CX_JSON_TOKEN_SPACE; } } @@ -644,6 +630,12 @@ void cxJsonDestroy(CxJson *json) { } } +void cxJsonReset(CxJson *json) { + const CxAllocator *allocator = json->allocator; + cxJsonDestroy(json); + cxJsonInit(json, allocator); +} + int cxJsonFilln(CxJson *json, const char *buf, size_t size) { if (cxBufferEof(&json->buffer)) { // reinitialize the buffer @@ -1126,10 +1118,53 @@ CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { return value->value.array.array[index]; } +CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { + if (index >= value->value.array.array_size) { + return NULL; + } + CxJsonValue *ret = value->value.array.array[index]; + // TODO: replace with a low level cx_array_remove() + size_t count = value->value.array.array_size - index - 1; + if (count > 0) { + memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*)); + } + value->value.array.array_size--; + return ret; +} + +char *cxJsonAsString(const CxJsonValue *value) { + return value->value.string.ptr; +} + +cxstring cxJsonAsCxString(const CxJsonValue *value) { + return cx_strcast(value->value.string); +} + +cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { + return value->value.string; +} + +double cxJsonAsDouble(const CxJsonValue *value) { + if (value->type == CX_JSON_INTEGER) { + return (double) value->value.integer; + } else { + return value->value.number; + } +} + +int64_t cxJsonAsInteger(const CxJsonValue *value) { + if (value->type == CX_JSON_INTEGER) { + return value->value.integer; + } else { + return (int64_t) value->value.number; + } +} + CxIterator cxJsonArrIter(const CxJsonValue *value) { return cxIteratorPtr( value->value.array.array, - value->value.array.array_size + value->value.array.array_size, + true // arrays need to keep order ); } @@ -1137,16 +1172,31 @@ CxIterator cxJsonObjIter(const CxJsonValue *value) { return cxIterator( value->value.object.values, sizeof(CxJsonObjValue), - value->value.object.values_size + value->value.object.values_size, + true // TODO: objects do not always need to keep order ); } -CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name) { - CxJsonObjValue *member = json_find_objvalue(value, name); - if (member == NULL) { +CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { + size_t index = json_find_objvalue(value, name); + if (index >= value->value.object.values_size) { return &cx_json_value_nothing; } else { - return member->value; + return value->value.object.values[index].value; + } +} + +CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { + size_t index = json_find_objvalue(value, name); + if (index >= value->value.object.values_size) { + return NULL; + } else { + CxJsonObjValue kv = value->value.object.values[index]; + cx_strfree_a(value->allocator, &kv.name); + // TODO: replace with cx_array_remove() / cx_array_remove_fast() + value->value.object.values_size--; + memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue)); + return kv.value; } } diff --git a/ucx/kv_list.c b/ucx/kv_list.c new file mode 100644 index 0000000..d4e19f2 --- /dev/null +++ b/ucx/kv_list.c @@ -0,0 +1,705 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 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/kv_list.h" +#include "cx/hash_map.h" +#include "cx/linked_list.h" + +#include +#include + +typedef struct cx_kv_list_s cx_kv_list; + +struct cx_kv_list_map_s { + struct cx_hash_map_s map_base; + /** Back-reference to the list. */ + cx_kv_list *list; +}; + +struct cx_kv_list_s { + struct cx_linked_list_s list; + /** The lookup map - stores pointers to the nodes. */ + struct cx_kv_list_map_s *map; + const cx_list_class *list_methods; + const cx_map_class *map_methods; + cx_destructor_func list_destr; + cx_destructor_func2 list_destr2; + void *list_destr_data; + cx_destructor_func map_destr; + cx_destructor_func2 map_destr2; + void *map_destr_data; +}; + +static void cx_kv_list_destructor_wrapper(void *list_ptr, void *elem) { + const cx_kv_list *kv_list = list_ptr; + // list destructor is already called with proper deref of the elem + if (kv_list->list_destr) { + kv_list->list_destr(elem); + } + if (kv_list->list_destr2) { + kv_list->list_destr2(kv_list->list_destr_data, elem); + } + if (kv_list->map_destr) { + kv_list->map_destr(elem); + } + if (kv_list->map_destr2) { + kv_list->map_destr2(kv_list->map_destr_data, elem); + } +} + +static void cx_kv_list_update_destructors(cx_kv_list *list) { + // we copy the destructors to our custom fields and register + // an own destructor function which invokes all these + if (list->list.base.collection.simple_destructor != NULL) { + list->list_destr = list->list.base.collection.simple_destructor; + list->list.base.collection.simple_destructor = NULL; + } + if (list->list.base.collection.advanced_destructor != cx_kv_list_destructor_wrapper) { + list->list_destr2 = list->list.base.collection.advanced_destructor; + list->list_destr_data = list->list.base.collection.destructor_data; + list->list.base.collection.advanced_destructor = cx_kv_list_destructor_wrapper; + list->list.base.collection.destructor_data = list; + } + if (list->map->map_base.base.collection.simple_destructor != NULL) { + list->map_destr = list->map->map_base.base.collection.simple_destructor; + list->map->map_base.base.collection.simple_destructor = NULL; + } + if (list->map->map_base.base.collection.advanced_destructor != NULL) { + list->map_destr2 = list->map->map_base.base.collection.advanced_destructor; + list->map_destr_data = list->map->map_base.base.collection.destructor_data; + list->map->map_base.base.collection.advanced_destructor = NULL; + list->map->map_base.base.collection.destructor_data = NULL; + } +} + +static CxHashKey *cx_kv_list_loc_key(cx_kv_list *list, void *node_data) { + return (CxHashKey*)((char*)node_data + list->list.base.collection.elem_size); +} + +static void cx_kvl_deallocate(struct cx_list_s *list) { + cx_kv_list *kv_list = (cx_kv_list*)list; + // patch the destructors + cx_kv_list_update_destructors(kv_list); + kv_list->map_methods->deallocate(&kv_list->map->map_base.base); + // then free the list, now the destructors may be called + kv_list->list_methods->deallocate(list); +} + +static void *cx_kvl_insert_element( + struct cx_list_s *list, + size_t index, + const void *data +) { + cx_kv_list *kv_list = (cx_kv_list*)list; + return kv_list->list_methods->insert_element(list, index, data); +} + +static size_t cx_kvl_insert_array( + struct cx_list_s *list, + size_t index, + const void *data, + size_t n +) { + cx_kv_list *kv_list = (cx_kv_list*)list; + return kv_list->list_methods->insert_array(list, index, data, n); +} + +static size_t cx_kvl_insert_sorted( + struct cx_list_s *list, + const void *sorted_data, + size_t n +) { + cx_kv_list *kv_list = (cx_kv_list*)list; + return kv_list->list_methods->insert_sorted(list, sorted_data, n); +} + +static size_t cx_kvl_insert_unique( + struct cx_list_s *list, + const void *sorted_data, + size_t n +) { + cx_kv_list *kv_list = (cx_kv_list*)list; + return kv_list->list_methods->insert_unique(list, sorted_data, n); +} + +static int cx_kvl_insert_iter( + struct cx_iterator_s *iter, + const void *elem, + int prepend +) { + cx_kv_list *kv_list = iter->src_handle; + return kv_list->list_methods->insert_iter(iter, elem, prepend); +} + +static size_t cx_kvl_remove( + struct cx_list_s *list, + size_t index, + size_t num, + void *targetbuf +) { + cx_kv_list *kv_list = (cx_kv_list*)list; + // patch the destructors + // we also have to do that when targetbuf is NULL, + // because we do not want wrong destructors to be called when we remove keys from the map + cx_kv_list_update_destructors(kv_list); + // iterate through the elements first and remove their keys from the map + CxIterator iter = kv_list->list_methods->iterator(list, index, false); + for (size_t i = 0; i < num && cxIteratorValid(iter); i++) { + void *node_data = cxIteratorCurrent(iter); + CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data); + // when the hash is zero, there is no key assigned to that element + if (key->hash != 0) { + kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL); + } + cxIteratorNext(iter); + } + return kv_list->list_methods->remove(list, index, num, targetbuf); +} + +static void cx_kvl_clear(struct cx_list_s *list) { + cx_kv_list *kv_list = (cx_kv_list*)list; + // patch the destructors + cx_kv_list_update_destructors(kv_list); + // clear the list + kv_list->list_methods->clear(list); + // then clear the map + kv_list->map_methods->clear(&kv_list->map->map_base.base); +} + +static int cx_kvl_swap( + struct cx_list_s *list, + size_t i, + size_t j +) { + cx_kv_list *kv_list = (cx_kv_list*)list; + return kv_list->list_methods->swap(list, i, j); +} + +static void *cx_kvl_at( + const struct cx_list_s *list, + size_t index +) { + const cx_kv_list *kv_list = (const cx_kv_list*)list; + return kv_list->list_methods->at(list, index); +} + +static size_t cx_kvl_find_remove( + struct cx_list_s *list, + const void *elem, + bool remove +) { + cx_kv_list *kv_list = (cx_kv_list*)list; + // we do not use the original list methods, + // because that would need two passes for removal + // (the first to find the index, the second to get a pointer) + if (list->collection.size == 0) return 0; + + size_t index; + cx_linked_list *ll = &kv_list->list; + char *node = cx_linked_list_find( + ll->begin, + ll->loc_next, ll->loc_data, + list->collection.cmpfunc, elem, + &index + ); + if (node == NULL) { + return list->collection.size; + } + if (remove) { + cx_kv_list_update_destructors(kv_list); + cx_invoke_advanced_destructor(list, node + ll->loc_data); + cx_linked_list_remove(&ll->begin, &ll->end, + ll->loc_prev, ll->loc_next, node); + CxHashKey *key = cx_kv_list_loc_key(kv_list, node + ll->loc_data); + if (key->hash != 0) { + kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL); + } + list->collection.size--; + cxFree(list->collection.allocator, node); + } + return index; +} + +static void cx_kvl_sort(struct cx_list_s *list) { + cx_kv_list *kv_list = (cx_kv_list*)list; + kv_list->list_methods->sort(list); +} + +static void cx_kvl_reverse(struct cx_list_s *list) { + cx_kv_list *kv_list = (cx_kv_list*)list; + kv_list->list_methods->reverse(list); +} + +static void cx_kvl_list_iter_next(void *it) { + struct cx_iterator_s *iter = it; + if (iter->base.remove) { + // remove the assigned key from the map before calling the actual function + cx_kv_list *kv_list = iter->src_handle; + cx_kv_list_update_destructors(kv_list); + char *node = iter->elem_handle; + CxHashKey *key = cx_kv_list_loc_key(kv_list, node + kv_list->list.loc_data); + if (key->hash != 0) { + kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL); + } + } + // note that we do not clear the remove flag, because the next_impl will do that + iter->base.next_impl(it); +} + +static struct cx_iterator_s cx_kvl_iterator( + const struct cx_list_s *list, + size_t index, + bool backward +) { + const cx_kv_list *kv_list = (const cx_kv_list*)list; + struct cx_iterator_s iter = kv_list->list_methods->iterator(list, index, backward); + iter.base.next_impl = iter.base.next; + iter.base.next = cx_kvl_list_iter_next; + return iter; +} + +static void cx_kvl_map_deallocate(struct cx_map_s *map) { + cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; + kv_list->map_methods->deallocate(map); + kv_list->list_methods->deallocate(&kv_list->list.base); +} + +static void cx_kvl_map_clear(struct cx_map_s *map) { + cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; + cx_kv_list_update_destructors(kv_list); + kv_list->list_methods->clear(&kv_list->list.base); + kv_list->map_methods->clear(map); +} + +static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) { + cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; + // if the hash has not yet been computed, do it now + if (key.hash == 0) { + cx_hash_murmur(&key); + } + + // reserve memory in the map first + void **map_data = kv_list->map_methods->put(map, key, NULL); + if (map_data == NULL) return NULL; // LCOV_EXCL_LINE + + // insert the data into the list (which most likely destroys the sorted property) + kv_list->list.base.collection.sorted = false; + void *node_data = kv_list->list_methods->insert_element( + &kv_list->list.base, kv_list->list.base.collection.size, + kv_list->list.base.collection.store_pointer ? &value : value); + if (node_data == NULL) { // LCOV_EXCL_START + // non-destructively remove the key again + kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data); + return NULL; + } // LCOV_EXCL_STOP + + // write the node pointer to the map entry + *map_data = node_data; + + // copy the key to the node data + CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data); + *key_ptr = key; + + // we must return node_data here and not map_data, + // because the node_data is the actual element of this collection + return node_data; +} + +void *cx_kvl_map_get(const CxMap *map, CxHashKey key) { + cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; + void *node_data = kv_list->map_methods->get(map, key); + if (node_data == NULL) return NULL; // LCOV_EXCL_LINE + // return the node data + return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data; +} + +int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) { + cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; + + void *node_data; + if (kv_list->map_methods->remove(map, key, &node_data)) { + return 1; + } + // we cannot just call a list method (because we don't have the index) + // and tbh. we also don't want to (because it's not performant when we + // can have the node ptr directly instead) + // therefore, we re-implement the logic ourselves + + // check if the outside caller want's us to return or to destroy the element + if (targetbuf == NULL) { + // patch the destructors and invoke them through the wrapper + cx_kv_list_update_destructors(kv_list); + cx_invoke_advanced_destructor(&kv_list->list.base, node_data); + } else { + // copy the element to the target buffer + memcpy(targetbuf, node_data, kv_list->list.base.collection.elem_size); + } + + // calculate the address of the node + void *node_ptr = (char*)node_data - kv_list->list.loc_data; + + // unlink the node + cx_linked_list_remove( + &kv_list->list.begin, + &kv_list->list.end, + kv_list->list.loc_prev, + kv_list->list.loc_next, + node_ptr + ); + + // decrement the list's size + kv_list->list.base.collection.size--; + + // deallocate the node + cxFree(kv_list->list.base.collection.allocator, node_ptr); + + return 0; +} + +static void *cx_kvl_iter_current_entry(const void *it) { + const CxMapIterator *iter = it; + return (void*)&iter->entry; +} + +static void *cx_kvl_iter_current_key(const void *it) { + const CxMapEntry *entry = cx_kvl_iter_current_entry(it); + return (void*)entry->key; +} + +static void *cx_kvl_iter_current_value(const void *it) { + const CxMapEntry *entry = cx_kvl_iter_current_entry(it); + return entry->value; +} + +static void cx_kvl_iter_next(void *it) { + CxMapIterator *iter = it; + cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)iter->map)->list; + + // find the next list entry that has a key assigned + CxHashKey *key = NULL; + char *next = iter->elem; + while (true) { + next = *(char**)(next + kv_list->list.loc_next); + if (next == NULL) break; + key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data); + if (key->hash != 0) break; + } + + // remove previous element if requested + if (iter->base.remove) { + iter->base.remove = false; + cx_kv_list_update_destructors(kv_list); + char *elem = iter->elem; + char *elem_data = elem + kv_list->list.loc_data; + CxHashKey *elem_key = cx_kv_list_loc_key(kv_list, elem_data); + // key is guaranteed to exist because iterator only iterates over elems with a key + kv_list->map_methods->remove(&kv_list->map->map_base.base, *elem_key, NULL); + cx_invoke_advanced_destructor(&kv_list->list.base, elem_data); + cx_linked_list_remove( + &kv_list->list.begin, + &kv_list->list.end, + kv_list->list.loc_prev, + kv_list->list.loc_next, + elem + ); + cxFree(kv_list->list.base.collection.allocator, elem); + kv_list->list.base.collection.size--; + iter->index--; + iter->elem_count--; + } + + // advance to the next element, if any + if (next == NULL) { + iter->index = kv_list->list.base.collection.size; + iter->elem = NULL; + iter->entry = (CxMapEntry){NULL, NULL}; + return; + } + iter->index++; + iter->elem = next; + iter->entry.key = key; + if (kv_list->list.base.collection.store_pointer) { + iter->entry.value = *(void**)(next + kv_list->list.loc_data); + } else { + iter->entry.value = (void*)(next + kv_list->list.loc_data); + } +} + +static bool cx_kvl_iter_valid(const void *it) { + const CxMapIterator *iter = it; + return iter->elem != NULL; +} + +CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) { + CxMapIterator iter = {0}; + + iter.type = type; + iter.map = (CxMap*)map; + // although we iterate over the list, we only report that many elements that have a key in the map + iter.elem_count = map->collection.size; + + switch (type) { + case CX_MAP_ITERATOR_PAIRS: + iter.elem_size = sizeof(CxMapEntry); + iter.base.current = cx_kvl_iter_current_entry; + break; + case CX_MAP_ITERATOR_KEYS: + iter.elem_size = sizeof(CxHashKey); + iter.base.current = cx_kvl_iter_current_key; + break; + case CX_MAP_ITERATOR_VALUES: + iter.elem_size = map->collection.elem_size; + iter.base.current = cx_kvl_iter_current_value; + break; + default: + assert(false); // LCOV_EXCL_LINE + } + + iter.base.allow_remove = true; + iter.base.next = cx_kvl_iter_next; + iter.base.valid = cx_kvl_iter_valid; + + // find the first list entry that has a key assigned + cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list; + CxHashKey *key = NULL; + char *next = kv_list->list.begin; + while (next != NULL) { + key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data); + if (key->hash != 0) break; + next = *(char**)(next + kv_list->list.loc_next); + } + if (next == NULL) { + iter.elem = NULL; + iter.entry = (CxMapEntry){NULL, NULL}; + } else { + iter.elem = next; + iter.entry.key = key; + if (kv_list->list.base.collection.store_pointer) { + iter.entry.value = *(void**)(next + kv_list->list.loc_data); + } else { + iter.entry.value = (void*)(next + kv_list->list.loc_data); + } + } + + return iter; +} + +static cx_list_class cx_kv_list_class = { + cx_kvl_deallocate, + cx_kvl_insert_element, + cx_kvl_insert_array, + cx_kvl_insert_sorted, + cx_kvl_insert_unique, + cx_kvl_insert_iter, + cx_kvl_remove, + cx_kvl_clear, + cx_kvl_swap, + cx_kvl_at, + cx_kvl_find_remove, + cx_kvl_sort, + NULL, + cx_kvl_reverse, + cx_kvl_iterator, +}; + +static cx_map_class cx_kv_map_class = { + cx_kvl_map_deallocate, + cx_kvl_map_clear, + cx_kvl_map_put, + cx_kvl_map_get, + cx_kvl_map_remove, + cx_kvl_map_iterator, +}; + +CxList *cxKvListCreate( + const CxAllocator *allocator, + cx_compare_func comparator, + size_t elem_size +) { + if (allocator == NULL) { + allocator = cxDefaultAllocator; + } + + // create a normal linked list and a normal hash map, first + CxList *list = cxLinkedListCreate(allocator, comparator, elem_size); + if (list == NULL) return NULL; // LCOV_EXCL_LINE + cx_linked_list *ll = (cx_linked_list*)list; + ll->extra_data_len = sizeof(CxHashKey); + CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0); + if (map == NULL) { // LCOV_EXCL_START + cxListFree(list); + return NULL; + } // LCOV_EXCL_STOP + + // patch the kv-list class with the compare function of the linked list + // this allows cxListCompare() to optimize comparisons between linked lists and kv-list + cx_kv_list_class.compare = list->cl->compare; + + // reallocate the map to add memory for the list back-reference + struct cx_kv_list_map_s *kv_map = cxRealloc(allocator, map, sizeof(struct cx_kv_list_map_s)); + + // reallocate the list to add memory for storing the metadata + cx_kv_list *kv_list = cxRealloc(allocator, list, sizeof(struct cx_kv_list_s)); + + // if any of the reallocations failed, we bail out + if (kv_map != NULL && kv_list != NULL) { + map = (CxMap*) kv_map; + list = (CxList*) kv_list; + } else { // LCOV_EXCL_START + cxListFree(list); + cxMapFree(map); + return NULL; + } // LCOV_EXCL_STOP + + // zero the custom destructor information + memset((char*)kv_list + offsetof(cx_kv_list, list_destr), 0, sizeof(void*)*6); + + // combine the list and the map aspect + kv_list->map = kv_map; + kv_map->list = kv_list; + + // remember the base methods and override them + kv_list->map_methods = map->cl; + map->cl = &cx_kv_map_class; + if (list->climpl == NULL) { + kv_list->list_methods = list->cl; + list->cl = &cx_kv_list_class; + } else { + kv_list->list_methods = list->climpl; + list->climpl = &cx_kv_list_class; + } + + return list; +} + +CxMap *cxKvListCreateAsMap( + const CxAllocator *allocator, + cx_compare_func comparator, + size_t elem_size +) { + CxList *list = cxKvListCreate(allocator, comparator, elem_size); + return list == NULL ? NULL : cxKvListAsMap(list); +} + +CxList *cxKvListAsList(CxMap *map) { + return &((struct cx_kv_list_map_s*)map)->list->list.base; +} + +CxMap *cxKvListAsMap(CxList *list) { + return &((cx_kv_list*)list)->map->map_base.base; +} + +int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key) { + cx_kv_list *kv_list = (cx_kv_list*)list; + void *node_data = kv_list->list_methods->at(list, index); + if (node_data == NULL) { + return 1; + } + // if the hash has not yet been computed, do it now + if (key.hash == 0) { + cx_hash_murmur(&key); + } + + // check if the key is already assigned + void *existing = kv_list->map_methods->get(&kv_list->map->map_base.base, key); + if (existing == node_data) { + return 0; // nothing to do + } + if (existing != NULL) { + // the key is already assigned to another node, we disallow re-assignment + return 1; + } + + // add the key to the map; + if (NULL == kv_list->map_methods->put(&kv_list->map->map_base.base, key, node_data)) { + return 1; // LCOV_EXCL_LINE + } + + // write the key to the list's node + CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); + *loc_key = key; + + return 0; +} + +int cxKvListRemoveKey(CxList *list, size_t index) { + cx_kv_list *kv_list = (cx_kv_list*)list; + void *node_data = kv_list->list_methods->at(list, index); + if (node_data == NULL) { + return 1; + } + CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); + if (loc_key->hash == 0) { + return 0; + } + kv_list->map_methods->remove(&kv_list->map->map_base.base, *loc_key, NULL); + // also zero the memory in the list node, + // but don't free the key data (that was done by the map remove) + memset(loc_key, 0, sizeof(CxHashKey)); + return 0; +} + +const CxHashKey *cxKvListGetKey(CxList *list, size_t index) { + cx_kv_list *kv_list = (cx_kv_list*)list; + void *node_data = kv_list->list_methods->at(list, index); + if (node_data == NULL) { + return NULL; + } + CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data); + if (key->hash == 0) { + return NULL; + } + return key; +} + +int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value) { + // assume we are losing the sorted property + list->collection.sorted = false; + + cx_kv_list *kv_list = (cx_kv_list*)list; + + // reserve memory in the map + void **map_data = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL); + if (map_data == NULL) return 1; // LCOV_EXCL_LINE + + // insert the node + void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index, + kv_list->list.base.collection.store_pointer ? &value : value); + if (node_data == NULL) { // LCOV_EXCL_START + // non-destructively remove the key again + kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data); + return 1; + } // LCOV_EXCL_STOP + *map_data = node_data; + + // write the key to the node + CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data); + *loc_key = key; + + return 0; +} diff --git a/ucx/linked_list.c b/ucx/linked_list.c index 7cd2ff2..370b349 100644 --- a/ucx/linked_list.c +++ b/ucx/linked_list.c @@ -244,80 +244,184 @@ void cx_linked_list_insert_sorted( begin, end, loc_prev, loc_next, new_node, cmp_func); } -void cx_linked_list_insert_sorted_chain( +static void *cx_linked_list_insert_sorted_chain_impl( void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, - cx_compare_func cmp_func + cx_compare_func cmp_func, + bool allow_duplicates ) { assert(begin != NULL); assert(loc_next >= 0); assert(insert_begin != NULL); - // track currently observed nodes - void *dest_prev = NULL; - void *dest = *begin; - void *src = insert_begin; - - // special case: list is empty - if (dest == NULL) { - *begin = src; - if (end != NULL) { - *end = cx_linked_list_last(src, loc_next); + // strategy: build completely new chains from scratch + void *source_original = *begin; + void *source_argument = insert_begin; + void *new_begin = NULL; + void *new_end = NULL; + void *dup_begin = NULL; + void *dup_end = NULL; + + // determine the new start + { + int d = source_original == NULL ? 1 : cmp_func(source_original, source_argument); + if (d <= 0) { + // the new chain starts with the original chain + new_begin = new_end = source_original; + source_original = ll_next(source_original); + if (d == 0) { + if (allow_duplicates) { + // duplicate allowed, append it to the chain + cx_linked_list_link(new_end, source_argument, loc_prev, loc_next); + new_end = source_argument; + } else { + // duplicate is not allowed, start a duplicate chain with the argument + dup_begin = dup_end = source_argument; + } + source_argument = ll_next(source_argument); + } + } else { + // input is smaller, or there is no original chain; + // start the new chain with the source argument + new_begin = new_end = source_argument; + source_argument = ll_next(source_argument); } - return; } - // search the list for insertion points - while (dest != NULL && src != NULL) { - // compare current list node with source node - // if less or equal, skip - if (cmp_func(dest, src) <= 0) { - dest_prev = dest; - dest = ll_next(dest); - continue; - } - - // determine chain of elements that can be inserted - void *end_of_chain = src; - void *next_in_chain = ll_next(src); - while (next_in_chain != NULL) { - // once we become larger than the list elem, break - if (cmp_func(dest, next_in_chain) <= 0) { - break; + // now successively compare the elements and add them to the correct chains + while (source_original != NULL && source_argument != NULL) { + int d = cmp_func(source_original, source_argument); + if (d <= 0) { + // the original is not larger, add it to the chain + cx_linked_list_link(new_end, source_original, loc_prev, loc_next); + new_end = source_original; + source_original = ll_next(source_original); + if (d == 0) { + if (allow_duplicates) { + // duplicate allowed, append it to the chain + cx_linked_list_link(new_end, source_argument, loc_prev, loc_next); + new_end = source_argument; + } else { + // duplicate is not allowed, append it to the duplicate chain + if (dup_end == NULL) { + dup_begin = dup_end = source_argument; + } else { + cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next); + dup_end = source_argument; + } + } + source_argument = ll_next(source_argument); + } + } else { + // the original is larger, append the source argument to the chain + // check if we must discard the source argument as duplicate + if (!allow_duplicates && cmp_func(new_end, source_argument) == 0) { + if (dup_end == NULL) { + dup_begin = dup_end = source_argument; + } else { + cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next); + dup_end = source_argument; + } + } else { + // no duplicate or duplicates allowed + cx_linked_list_link(new_end, source_argument, loc_prev, loc_next); + new_end = source_argument; } - // otherwise, we can insert one more - end_of_chain = next_in_chain; - next_in_chain = ll_next(next_in_chain); + source_argument = ll_next(source_argument); } + } - // insert the elements - if (dest_prev == NULL) { - // new begin - *begin = src; + if (source_original != NULL) { + // something is left from the original chain, append it + cx_linked_list_link(new_end, source_original, loc_prev, loc_next); + new_end = cx_linked_list_last(source_original, loc_next); + } else if (source_argument != NULL) { + // something is left from the input chain; + // when we allow duplicates, append it + if (allow_duplicates) { + cx_linked_list_link(new_end, source_argument, loc_prev, loc_next); + new_end = cx_linked_list_last(source_argument, loc_next); } else { - cx_linked_list_link(dest_prev, src, loc_prev, loc_next); + // otherwise we must check one-by-one + while (source_argument != NULL) { + if (cmp_func(new_end, source_argument) == 0) { + if (dup_end == NULL) { + dup_begin = dup_end = source_argument; + } else { + cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next); + dup_end = source_argument; + } + } else { + cx_linked_list_link(new_end, source_argument, loc_prev, loc_next); + new_end = source_argument; + } + source_argument = ll_next(source_argument); + } } - cx_linked_list_link(end_of_chain, dest, loc_prev, loc_next); + } - // continue with next - src = next_in_chain; - dest_prev = dest; - dest = ll_next(dest); + // null the next pointers at the end of the chain + ll_next(new_end) = NULL; + if (dup_end != NULL) { + ll_next(dup_end) = NULL; } - // insert remaining items - if (src != NULL) { - cx_linked_list_link(dest_prev, src, loc_prev, loc_next); + // null the optional prev pointers + if (loc_prev >= 0) { + ll_prev(new_begin) = NULL; + if (dup_begin != NULL) { + ll_prev(dup_begin) = NULL; + } } - // determine new end of list, if requested + // output + *begin = new_begin; if (end != NULL) { - *end = cx_linked_list_last( - dest != NULL ? dest : dest_prev, loc_next); + *end = new_end; } + return dup_begin; +} + +void cx_linked_list_insert_sorted_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *insert_begin, + cx_compare_func cmp_func +) { + cx_linked_list_insert_sorted_chain_impl( + begin, end, loc_prev, loc_next, + insert_begin, cmp_func, true); +} + +int cx_linked_list_insert_unique( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node, + cx_compare_func cmp_func +) { + assert(ll_next(new_node) == NULL); + return NULL != cx_linked_list_insert_unique_chain( + begin, end, loc_prev, loc_next, new_node, cmp_func); +} + +void *cx_linked_list_insert_unique_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *insert_begin, + cx_compare_func cmp_func +) { + return cx_linked_list_insert_sorted_chain_impl( + begin, end, loc_prev, loc_next, + insert_begin, cmp_func, false); } size_t cx_linked_list_remove_chain( @@ -370,6 +474,16 @@ size_t cx_linked_list_remove_chain( return removed; } +void cx_linked_list_remove( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node +) { + cx_linked_list_remove_chain(begin, end, loc_prev, loc_next, node, 1); +} + size_t cx_linked_list_size( const void *node, ptrdiff_t loc_next @@ -564,64 +678,46 @@ void cx_linked_list_reverse( // HIGH LEVEL LINKED LIST IMPLEMENTATION -typedef struct cx_linked_list_node cx_linked_list_node; -struct cx_linked_list_node { - cx_linked_list_node *prev; - cx_linked_list_node *next; - char payload[]; -}; - -#define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev) -#define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next) -#define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload) - -typedef struct { - struct cx_list_s base; - cx_linked_list_node *begin; - cx_linked_list_node *end; -} cx_linked_list; - -static cx_linked_list_node *cx_ll_node_at( +static void *cx_ll_node_at( const cx_linked_list *list, size_t index ) { if (index >= list->base.collection.size) { return NULL; } else if (index > list->base.collection.size / 2) { - return cx_linked_list_at(list->end, list->base.collection.size - 1, CX_LL_LOC_PREV, index); + return cx_linked_list_at(list->end, list->base.collection.size - 1, list->loc_prev, index); } else { - return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index); + return cx_linked_list_at(list->begin, 0, list->loc_next, index); } } -static cx_linked_list_node *cx_ll_malloc_node(const struct cx_list_s *list) { - return cxMalloc(list->collection.allocator, - sizeof(cx_linked_list_node) + list->collection.elem_size); +static void *cx_ll_malloc_node(const cx_linked_list *list) { + return cxZalloc(list->base.collection.allocator, + list->loc_data + list->base.collection.elem_size + list->extra_data_len); } static int cx_ll_insert_at( struct cx_list_s *list, - cx_linked_list_node *node, + void *node, const void *elem ) { + cx_linked_list *ll = (cx_linked_list *) list; // create the new new_node - cx_linked_list_node *new_node = cx_ll_malloc_node(list); + void *new_node = cx_ll_malloc_node(ll); // sortir if failed if (new_node == NULL) return 1; - // initialize new new_node - new_node->prev = new_node->next = NULL; + // copy the data if (elem != NULL) { - memcpy(new_node->payload, elem, list->collection.elem_size); + memcpy((char*)new_node + ll->loc_data, elem, list->collection.elem_size); } // insert - cx_linked_list *ll = (cx_linked_list *) list; cx_linked_list_insert_chain( - (void **) &ll->begin, (void **) &ll->end, - CX_LL_LOC_PREV, CX_LL_LOC_NEXT, + &ll->begin, &ll->end, + ll->loc_prev, ll->loc_next, node, new_node, new_node ); @@ -640,7 +736,7 @@ static size_t cx_ll_insert_array( if (index > list->collection.size || n == 0) return 0; // find position efficiently - cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); + void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); // perform first insert if (0 != cx_ll_insert_at(list, node, array)) return 1; @@ -649,14 +745,17 @@ static size_t cx_ll_insert_array( if (n == 1) return 1; // we now know exactly where we are - node = node == NULL ? ((cx_linked_list *) list)->begin : node->next; + cx_linked_list *ll = (cx_linked_list *) list; + node = node == NULL ? ((cx_linked_list *) list)->begin : CX_LL_PTR(node, ll->loc_next); // we can add the remaining nodes and immediately advance to the inserted node const char *source = array; for (size_t i = 1; i < n; i++) { - source += list->collection.elem_size; + if (source != NULL) { + source += list->collection.elem_size; + } if (0 != cx_ll_insert_at(list, node, source)) return i; - node = node->next; + node = CX_LL_PTR(node, ll->loc_next); } return n; } @@ -670,76 +769,113 @@ static void *cx_ll_insert_element( if (index > list->collection.size) return NULL; // find position efficiently - cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); + void *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); // perform first insert if (cx_ll_insert_at(list, node, element)) return NULL; // return a pointer to the data of the inserted node + cx_linked_list *ll = (cx_linked_list *) list; if (node == NULL) { - return ((cx_linked_list *) list)->begin->payload; + return (char*)(ll->begin) + ll->loc_data; } else { - return node->next->payload; + char *next = CX_LL_PTR(node, ll->loc_next); + return next + ll->loc_data; } } static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func; +static _Thread_local off_t cx_ll_insert_sorted_loc_data; static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) { - const cx_linked_list_node *left = l; - const cx_linked_list_node *right = r; - return cx_ll_insert_sorted_cmp_func(left->payload, right->payload); + const char *left = (const char*)l + cx_ll_insert_sorted_loc_data; + const char *right = (const char*)r + cx_ll_insert_sorted_loc_data; + return cx_ll_insert_sorted_cmp_func(left, right); } -static size_t cx_ll_insert_sorted( +static size_t cx_ll_insert_sorted_impl( struct cx_list_s *list, const void *array, - size_t n + size_t n, + bool allow_duplicates ) { + cx_linked_list *ll = (cx_linked_list *) list; + // special case if (n == 0) return 0; // create a new chain of nodes - cx_linked_list_node *chain = cx_ll_malloc_node(list); + void *chain = cx_ll_malloc_node(ll); if (chain == NULL) return 0; - memcpy(chain->payload, array, list->collection.elem_size); - chain->prev = NULL; - chain->next = NULL; + memcpy((char*)chain + ll->loc_data, array, list->collection.elem_size); // add all elements from the array to that chain - cx_linked_list_node *prev = chain; + void *prev = chain; const char *src = array; size_t inserted = 1; for (; inserted < n; inserted++) { - cx_linked_list_node *next = cx_ll_malloc_node(list); + void *next = cx_ll_malloc_node(ll); if (next == NULL) break; src += list->collection.elem_size; - memcpy(next->payload, src, list->collection.elem_size); - prev->next = next; - next->prev = prev; + memcpy((char*)next + ll->loc_data, src, list->collection.elem_size); + CX_LL_PTR(prev, ll->loc_next) = next; + CX_LL_PTR(next, ll->loc_prev) = prev; prev = next; } - prev->next = NULL; + CX_LL_PTR(prev, ll->loc_next) = NULL; // invoke the low level function - cx_linked_list *ll = (cx_linked_list *) list; cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc; - cx_linked_list_insert_sorted_chain( - (void **) &ll->begin, - (void **) &ll->end, - CX_LL_LOC_PREV, - CX_LL_LOC_NEXT, - chain, - cx_ll_insert_sorted_cmp_helper - ); - - // adjust the list metadata - list->collection.size += inserted; + cx_ll_insert_sorted_loc_data = ll->loc_data; + if (allow_duplicates) { + cx_linked_list_insert_sorted_chain( + &ll->begin, + &ll->end, + ll->loc_prev, + ll->loc_next, + chain, + cx_ll_insert_sorted_cmp_helper + ); + list->collection.size += inserted; + } else { + void *duplicates = cx_linked_list_insert_unique_chain( + &ll->begin, + &ll->end, + ll->loc_prev, + ll->loc_next, + chain, + cx_ll_insert_sorted_cmp_helper + ); + list->collection.size += inserted; + // free the nodes that did not make it into the list + while (duplicates != NULL) { + void *next = CX_LL_PTR(duplicates, ll->loc_next); + cxFree(list->collection.allocator, duplicates); + duplicates = next; + list->collection.size--; + } + } return inserted; } +static size_t cx_ll_insert_sorted( + struct cx_list_s *list, + const void *array, + size_t n +) { + return cx_ll_insert_sorted_impl(list, array, n, true); +} + +static size_t cx_ll_insert_unique( + struct cx_list_s *list, + const void *array, + size_t n +) { + return cx_ll_insert_sorted_impl(list, array, n, false); +} + static size_t cx_ll_remove( struct cx_list_s *list, size_t index, @@ -747,7 +883,7 @@ static size_t cx_ll_remove( void *targetbuf ) { cx_linked_list *ll = (cx_linked_list *) list; - cx_linked_list_node *node = cx_ll_node_at(ll, index); + void *node = cx_ll_node_at(ll, index); // out-of-bounds check if (node == NULL) return 0; @@ -756,8 +892,8 @@ static size_t cx_ll_remove( size_t removed = cx_linked_list_remove_chain( (void **) &ll->begin, (void **) &ll->end, - CX_LL_LOC_PREV, - CX_LL_LOC_NEXT, + ll->loc_prev, + ll->loc_next, node, num ); @@ -767,28 +903,28 @@ static size_t cx_ll_remove( // copy or destroy the removed chain if (targetbuf == NULL) { - cx_linked_list_node *n = node; + char *n = node; for (size_t i = 0; i < removed; i++) { // element destruction - cx_invoke_destructor(list, n->payload); + cx_invoke_destructor(list, n + ll->loc_data); // free the node and advance - void *next = n->next; + void *next = CX_LL_PTR(n, ll->loc_next); cxFree(list->collection.allocator, n); n = next; } } else { char *dest = targetbuf; - cx_linked_list_node *n = node; + char *n = node; for (size_t i = 0; i < removed; i++) { // copy payload - memcpy(dest, n->payload, list->collection.elem_size); + memcpy(dest, n + ll->loc_data, list->collection.elem_size); // advance target buffer dest += list->collection.elem_size; // free the node and advance - void *next = n->next; + void *next = CX_LL_PTR(n, ll->loc_next); cxFree(list->collection.allocator, n); n = next; } @@ -801,10 +937,10 @@ static void cx_ll_clear(struct cx_list_s *list) { if (list->collection.size == 0) return; cx_linked_list *ll = (cx_linked_list *) list; - cx_linked_list_node *node = ll->begin; + char *node = ll->begin; while (node != NULL) { - cx_invoke_destructor(list, node->payload); - cx_linked_list_node *next = node->next; + cx_invoke_destructor(list, node + ll->loc_data); + void *next = CX_LL_PTR(node, ll->loc_next); cxFree(list->collection.allocator, node); node = next; } @@ -831,14 +967,14 @@ static int cx_ll_swap( left = j; right = i; } - cx_linked_list_node *nleft = NULL, *nright = NULL; + void *nleft = NULL, *nright = NULL; 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; + nright = CX_LL_PTR(nright, ll->loc_next); } } else if (left >= mid && right >= mid) { // case 2: both items right from mid @@ -846,7 +982,7 @@ static int cx_ll_swap( assert(nright != NULL); nleft = nright; for (size_t c = right; c > left; c--) { - nleft = nleft->prev; + nleft = CX_LL_PTR(nleft, ll->loc_prev); } } else { // case 3: one item left, one item right @@ -872,12 +1008,12 @@ static int cx_ll_swap( if (closest == left) { nright = nleft; for (size_t c = left; c < right; c++) { - nright = nright->next; + nright = CX_LL_PTR(nright, ll->loc_next); } } else { nleft = nright; for (size_t c = right; c > left; c--) { - nleft = nleft->prev; + nleft = CX_LL_PTR(nleft, ll->loc_prev); } } } else { @@ -890,33 +1026,33 @@ static int cx_ll_swap( } } - cx_linked_list_node *prev = nleft->prev; - cx_linked_list_node *next = nright->next; - cx_linked_list_node *midstart = nleft->next; - cx_linked_list_node *midend = nright->prev; + void *prev = CX_LL_PTR(nleft, ll->loc_prev); + void *next = CX_LL_PTR(nright, ll->loc_next); + void *midstart = CX_LL_PTR(nleft, ll->loc_next); + void *midend = CX_LL_PTR(nright, ll->loc_prev); if (prev == NULL) { ll->begin = nright; } else { - prev->next = nright; + CX_LL_PTR(prev, ll->loc_next) = nright; } - nright->prev = prev; + CX_LL_PTR(nright, ll->loc_prev) = prev; if (midstart == nright) { // special case: both nodes are adjacent - nright->next = nleft; - nleft->prev = nright; + CX_LL_PTR(nright, ll->loc_next) = nleft; + CX_LL_PTR(nleft, ll->loc_prev) = nright; } else { // likely case: a chain is between the two nodes - nright->next = midstart; - midstart->prev = nright; - midend->next = nleft; - nleft->prev = midend; + CX_LL_PTR(nright, ll->loc_next) = midstart; + CX_LL_PTR(midstart, ll->loc_prev) = nright; + CX_LL_PTR(midend, ll->loc_next) = nleft; + CX_LL_PTR(nleft, ll->loc_prev) = midend; } - nleft->next = next; + CX_LL_PTR(nleft, ll->loc_next) = next; if (next == NULL) { ll->end = nleft; } else { - next->prev = nleft; + CX_LL_PTR(next, ll->loc_prev) = nleft; } return 0; @@ -927,8 +1063,8 @@ static void *cx_ll_at( size_t index ) { cx_linked_list *ll = (cx_linked_list *) list; - cx_linked_list_node *node = cx_ll_node_at(ll, index); - return node == NULL ? NULL : node->payload; + char *node = cx_ll_node_at(ll, index); + return node == NULL ? NULL : node + ll->loc_data; } static size_t cx_ll_find_remove( @@ -939,10 +1075,10 @@ static size_t cx_ll_find_remove( if (list->collection.size == 0) return 0; size_t index; - cx_linked_list *ll = ((cx_linked_list *) list); - cx_linked_list_node *node = cx_linked_list_find( + cx_linked_list *ll = (cx_linked_list *) list; + char *node = cx_linked_list_find( ll->begin, - CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + ll->loc_next, ll->loc_data, list->collection.cmpfunc, elem, &index ); @@ -950,9 +1086,9 @@ static size_t cx_ll_find_remove( return list->collection.size; } if (remove) { - 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); + cx_invoke_destructor(list, node + ll->loc_data); + cx_linked_list_remove(&ll->begin, &ll->end, + ll->loc_prev, ll->loc_next, node); list->collection.size--; cxFree(list->collection.allocator, node); } @@ -961,14 +1097,14 @@ static size_t cx_ll_find_remove( static void cx_ll_sort(struct cx_list_s *list) { cx_linked_list *ll = (cx_linked_list *) list; - cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end, - CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + cx_linked_list_sort(&ll->begin, &ll->end, + ll->loc_prev, ll->loc_next, ll->loc_data, list->collection.cmpfunc); } static void cx_ll_reverse(struct cx_list_s *list) { cx_linked_list *ll = (cx_linked_list *) list; - cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT); + cx_linked_list_reverse(&ll->begin, &ll->end, ll->loc_prev, ll->loc_next); } static int cx_ll_compare( @@ -977,8 +1113,10 @@ static int cx_ll_compare( ) { cx_linked_list *left = (cx_linked_list *) list; cx_linked_list *right = (cx_linked_list *) other; + assert(left->loc_next == right->loc_next); + assert(left->loc_data == right->loc_data); return cx_linked_list_compare(left->begin, right->begin, - CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + left->loc_next, left->loc_data, list->collection.cmpfunc); } @@ -989,49 +1127,52 @@ static bool cx_ll_iter_valid(const void *it) { static void cx_ll_iter_next(void *it) { struct cx_iterator_s *iter = it; + cx_linked_list *ll = iter->src_handle; if (iter->base.remove) { iter->base.remove = false; - struct cx_list_s *list = iter->src_handle.m; - cx_linked_list *ll = iter->src_handle.m; - cx_linked_list_node *node = iter->elem_handle; - iter->elem_handle = node->next; - 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); + struct cx_list_s *list = iter->src_handle; + char *node = iter->elem_handle; + iter->elem_handle = CX_LL_PTR(node, ll->loc_next); + cx_invoke_destructor(list, node + ll->loc_data); + cx_linked_list_remove(&ll->begin, &ll->end, + ll->loc_prev, ll->loc_next, node); list->collection.size--; + iter->elem_count--; cxFree(list->collection.allocator, node); } else { iter->index++; - cx_linked_list_node *node = iter->elem_handle; - iter->elem_handle = node->next; + void *node = iter->elem_handle; + iter->elem_handle = CX_LL_PTR(node, ll->loc_next); } } static void cx_ll_iter_prev(void *it) { struct cx_iterator_s *iter = it; + cx_linked_list *ll = iter->src_handle; if (iter->base.remove) { iter->base.remove = false; - struct cx_list_s *list = iter->src_handle.m; - cx_linked_list *ll = iter->src_handle.m; - cx_linked_list_node *node = iter->elem_handle; - iter->elem_handle = node->prev; + struct cx_list_s *list = iter->src_handle; + char *node = iter->elem_handle; + iter->elem_handle = CX_LL_PTR(node, ll->loc_prev); iter->index--; - 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); + cx_invoke_destructor(list, node + ll->loc_data); + cx_linked_list_remove(&ll->begin, &ll->end, + ll->loc_prev, ll->loc_next, node); list->collection.size--; + iter->elem_count--; cxFree(list->collection.allocator, node); } else { iter->index--; - cx_linked_list_node *node = iter->elem_handle; - iter->elem_handle = node->prev; + char *node = iter->elem_handle; + iter->elem_handle = CX_LL_PTR(node, ll->loc_prev); } } static void *cx_ll_iter_current(const void *it) { const struct cx_iterator_s *iter = it; - cx_linked_list_node *node = iter->elem_handle; - return node->payload; + const cx_linked_list *ll = iter->src_handle; + char *node = iter->elem_handle; + return node + ll->loc_data; } static CxIterator cx_ll_iterator( @@ -1041,14 +1182,14 @@ static CxIterator cx_ll_iterator( ) { CxIterator iter; iter.index = index; - iter.src_handle.c = list; + iter.src_handle = (void*)list; iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index); iter.elem_size = list->collection.elem_size; iter.elem_count = list->collection.size; iter.base.valid = cx_ll_iter_valid; iter.base.current = cx_ll_iter_current; iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; - iter.base.mutating = false; + iter.base.allow_remove = true; iter.base.remove = false; return iter; } @@ -1058,11 +1199,12 @@ static int cx_ll_insert_iter( const void *elem, int prepend ) { - struct cx_list_s *list = iter->src_handle.m; - cx_linked_list_node *node = iter->elem_handle; + struct cx_list_s *list = iter->src_handle; + cx_linked_list *ll = iter->src_handle; + void *node = iter->elem_handle; if (node != NULL) { assert(prepend >= 0 && prepend <= 1); - cx_linked_list_node *choice[2] = {node, node->prev}; + void *choice[2] = {node, CX_LL_PTR(node, ll->loc_prev)}; int result = cx_ll_insert_at(list, choice[prepend], elem); if (result == 0) { iter->elem_count++; @@ -1084,10 +1226,10 @@ static int cx_ll_insert_iter( static void cx_ll_destructor(CxList *list) { cx_linked_list *ll = (cx_linked_list *) list; - cx_linked_list_node *node = ll->begin; + char *node = ll->begin; while (node) { - cx_invoke_destructor(list, node->payload); - void *next = node->next; + cx_invoke_destructor(list, node + ll->loc_data); + void *next = CX_LL_PTR(node, ll->loc_next); cxFree(list->collection.allocator, node); node = next; } @@ -1100,6 +1242,7 @@ static cx_list_class cx_linked_list_class = { cx_ll_insert_element, cx_ll_insert_array, cx_ll_insert_sorted, + cx_ll_insert_unique, cx_ll_insert_iter, cx_ll_remove, cx_ll_clear, @@ -1123,6 +1266,10 @@ CxList *cxLinkedListCreate( cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); if (list == NULL) return NULL; + list->extra_data_len = 0; + list->loc_prev = 0; + list->loc_next = sizeof(void*); + list->loc_data = sizeof(void*)*2; cx_list_init((CxList*)list, &cx_linked_list_class, allocator, comparator, elem_size); diff --git a/ucx/list.c b/ucx/list.c index 7fbbb8b..d4a8b22 100644 --- a/ucx/list.c +++ b/ucx/list.c @@ -29,6 +29,7 @@ #include "cx/list.h" #include +#include // @@ -38,10 +39,18 @@ static int cx_pl_cmpfunc( const void *l, const void *r ) { + // l and r are guaranteed to be non-NULL pointing to the list's memory void *const *lptr = l; void *const *rptr = r; - const void *left = lptr == NULL ? NULL : *lptr; - const void *right = rptr == NULL ? NULL : *rptr; + const void *left = *lptr; + const void *right = *rptr; + if (left == NULL) { + // NULL is smaller than any value except NULL + return right == NULL ? 0 : -1; + } else if (right == NULL) { + // any value is larger than NULL + return 1; + } return cx_pl_cmpfunc_impl(left, right); } @@ -90,12 +99,23 @@ static size_t cx_pl_insert_sorted( return result; } +static size_t cx_pl_insert_unique( + struct cx_list_s *list, + const void *array, + size_t n +) { + cx_pl_hack_cmpfunc(list); + size_t result = list->climpl->insert_unique(list, array, n); + cx_pl_unhack_cmpfunc(list); + return result; +} + static int cx_pl_insert_iter( struct cx_iterator_s *iter, const void *elem, int prepend ) { - struct cx_list_s *list = iter->src_handle.m; + struct cx_list_s *list = iter->src_handle; return list->climpl->insert_iter(iter, &elem, prepend); } @@ -181,6 +201,7 @@ static cx_list_class cx_pointer_list_class = { cx_pl_insert_element, cx_pl_insert_array, cx_pl_insert_sorted, + cx_pl_insert_unique, cx_pl_insert_iter, cx_pl_remove, cx_pl_clear, @@ -225,7 +246,7 @@ static CxIterator cx_emptyl_iterator( cx_attr_unused bool backwards ) { CxIterator iter = {0}; - iter.src_handle.c = list; + iter.src_handle = (void*) list; iter.index = index; iter.base.valid = cx_emptyl_iter_valid; return iter; @@ -238,6 +259,7 @@ static cx_list_class cx_empty_list_class = { NULL, NULL, NULL, + NULL, cx_emptyl_noop, NULL, cx_emptyl_at, @@ -278,21 +300,26 @@ size_t cx_list_default_insert_array( const void *data, size_t n ) { - size_t elem_size = list->collection.elem_size; const char *src = data; size_t i = 0; for (; i < n; i++) { if (NULL == invoke_list_func( - insert_element, list, index + i, - src + (i * elem_size))) return i; + insert_element, list, index + i, src) + ) { + return i; // LCOV_EXCL_LINE + } + if (src != NULL) { + src += list->collection.elem_size; + } } return i; } -size_t cx_list_default_insert_sorted( +static size_t cx_list_default_insert_sorted_impl( struct cx_list_s *list, const void *sorted_data, - size_t n + size_t n, + bool allow_duplicates ) { // corner case if (n == 0) return 0; @@ -302,22 +329,54 @@ size_t cx_list_default_insert_sorted( const char *src = sorted_data; // track indices and number of inserted items - size_t di = 0, si = 0, inserted = 0; + size_t di = 0, si = 0, processed = 0; // search the list for insertion points - for (; di < list->collection.size; di++) { + while (di < list->collection.size) { const void *list_elm = invoke_list_func(at, list, di); - // compare current list element with first source element - // if less or equal, skip - if (cmp(list_elm, src) <= 0) { - continue; + // compare the current list element with the first source element + // if less, skip the list elements + // if equal, skip the list elements and optionally the source elements + { + int d = cmp(list_elm, src); + if (d <= 0) { + if (!allow_duplicates && d == 0) { + src += elem_size; + si++; + processed++; // we also count duplicates for the return value + while (si < n && cmp(list_elm, src) == 0) { + src += elem_size; + si++; + processed++; + } + if (processed == n) { + return processed; + } + } + di++; + continue; + } } - // determine number of consecutive elements that can be inserted - size_t ins = 1; + // determine the number of consecutive elements that can be inserted + size_t ins = 1, skip = 0; const char *next = src; while (++si < n) { + if (!allow_duplicates) { + // skip duplicates within the source + if (cmp(next, next + elem_size) == 0) { + next += elem_size; + skip++; + continue; + } else { + if (skip > 0) { + // if we had to skip something, we must wait for the next run + next += elem_size; + break; + } + } + } next += elem_size; // once we become larger than the list elem, break if (cmp(list_elm, next) <= 0) { @@ -329,33 +388,70 @@ size_t cx_list_default_insert_sorted( // insert the elements at location si if (ins == 1) { - if (NULL == invoke_list_func( - insert_element, list, di, src)) return inserted; + if (NULL == invoke_list_func(insert_element, list, di, src)) { + return processed; // LCOV_EXCL_LINE + } } else { size_t r = invoke_list_func(insert_array, list, di, src, ins); - if (r < ins) return inserted + r; + if (r < ins) { + return processed + r; // LCOV_EXCL_LINE + } } - inserted += ins; + processed += ins + skip; di += ins; // everything inserted? - if (inserted == n) return inserted; + if (processed == n) { + return processed; + } src = next; } // insert remaining items if (si < n) { - inserted += invoke_list_func(insert_array, list, di, src, n - si); + if (allow_duplicates) { + processed += invoke_list_func(insert_array, list, di, src, n - si); + } else { + const void *last = di == 0 ? NULL : invoke_list_func(at, list, di - 1); + for (; si < n; si++) { + // skip duplicates within the source + if (last == NULL || cmp(last, src) != 0) { + if (NULL == invoke_list_func(insert_element, list, di, src)) { + return processed; // LCOV_EXCL_LINE + } + last = src; + di++; + } + processed++; + src += elem_size; + } + } } - return inserted; + return processed; +} + +size_t cx_list_default_insert_sorted( + struct cx_list_s *list, + const void *sorted_data, + size_t n +) { + return cx_list_default_insert_sorted_impl(list, sorted_data, n, true); +} + +size_t cx_list_default_insert_unique( + struct cx_list_s *list, + const void *sorted_data, + size_t n +) { + return cx_list_default_insert_sorted_impl(list, sorted_data, n, false); } void cx_list_default_sort(struct cx_list_s *list) { size_t elem_size = list->collection.elem_size; size_t list_size = list->collection.size; void *tmp = cxMallocDefault(elem_size * list_size); - if (tmp == NULL) abort(); + if (tmp == NULL) abort(); // LCOV_EXCL_LINE // copy elements from source array char *loc = tmp; @@ -388,7 +484,7 @@ int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) { size_t elem_size = list->collection.elem_size; void *tmp = cxMallocDefault(elem_size); - if (tmp == NULL) return 1; + if (tmp == NULL) return 1; // LCOV_EXCL_LINE void *ip = invoke_list_func(at, list, i); void *jp = invoke_list_func(at, list, j); @@ -472,34 +568,174 @@ int cxListCompare( } } -CxIterator cxListMutIteratorAt( - CxList *list, - size_t index -) { - CxIterator it = list->cl->iterator(list, index, false); - it.base.mutating = true; - return it; +size_t cxListSize(const CxList *list) { + return list->collection.size; } -CxIterator cxListMutBackwardsIteratorAt( - CxList *list, - size_t index -) { - CxIterator it = list->cl->iterator(list, index, true); - it.base.mutating = true; - return it; +int cxListAdd(CxList *list, const void *elem) { + list->collection.sorted = false; + return list->cl->insert_element(list, list->collection.size, elem) == NULL; } -void cxListFree(CxList *list) { - if (list == NULL) return; - list->cl->deallocate(list); +size_t cxListAddArray(CxList *list, const void *array, size_t n) { + list->collection.sorted = false; + return list->cl->insert_array(list, list->collection.size, array, n); } -int cxListSet( - CxList *list, - size_t index, - const void *elem -) { +int cxListInsert(CxList *list, size_t index, const void *elem) { + list->collection.sorted = false; + return list->cl->insert_element(list, index, elem) == NULL; +} + +void *cxListEmplaceAt(CxList *list, size_t index) { + list->collection.sorted = false; + return list->cl->insert_element(list, index, NULL); +} + +void *cxListEmplace(CxList *list) { + list->collection.sorted = false; + return list->cl->insert_element(list, list->collection.size, NULL); +} + +static bool cx_list_emplace_iterator_valid(const void *it) { + const CxIterator *iter = it; + return iter->index < iter->elem_count; +} + +CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n) { + list->collection.sorted = false; + size_t c = list->cl->insert_array(list, index, NULL, n); + CxIterator iter = list->cl->iterator(list, index, false); + // tweak the fields of this iterator + iter.elem_count = c; + iter.index = 0; + // replace the valid function to abort iteration when c is reached + iter.base.valid = cx_list_emplace_iterator_valid; + // if we are storing pointers, we want to return the pure pointers. + // therefore, we must unwrap the "current" method + if (list->collection.store_pointer) { + iter.base.current = iter.base.current_impl; + } + return iter; +} + +CxIterator cxListEmplaceArray(CxList *list, size_t n) { + return cxListEmplaceArrayAt(list, list->collection.size, n); +} + +int cxListInsertSorted(CxList *list, const void *elem) { + assert(cxCollectionSorted(list)); + list->collection.sorted = true; + const void *data = list->collection.store_pointer ? &elem : elem; + return list->cl->insert_sorted(list, data, 1) == 0; +} + +int cxListInsertUnique(CxList *list, const void *elem) { + if (cxCollectionSorted(list)) { + list->collection.sorted = true; + const void *data = list->collection.store_pointer ? &elem : elem; + return list->cl->insert_unique(list, data, 1) == 0; + } else { + if (cxListContains(list, elem)) { + return 0; + } else { + return cxListAdd(list, elem); + } + } +} + +size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n) { + list->collection.sorted = false; + return list->cl->insert_array(list, index, array, n); +} + +size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n) { + assert(cxCollectionSorted(list)); + list->collection.sorted = true; + return list->cl->insert_sorted(list, array, n); +} + +size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n) { + if (cxCollectionSorted(list)) { + list->collection.sorted = true; + return list->cl->insert_unique(list, array, n); + } else { + const char *source = array; + for (size_t i = 0 ; i < n; i++) { + // note: this also checks elements added in a previous iteration + const void *data = list->collection.store_pointer ? + *((const void**)source) : source; + if (!cxListContains(list, data)) { + if (cxListAdd(list, data)) { + return i; // LCOV_EXCL_LINE + } + } + source += list->collection.elem_size; + } + return n; + } +} + +int cxListInsertAfter(CxIterator *iter, const void *elem) { + CxList* list = (CxList*)iter->src_handle; + list->collection.sorted = false; + return list->cl->insert_iter(iter, elem, 0); +} + +int cxListInsertBefore(CxIterator *iter, const void *elem) { + CxList* list = (CxList*)iter->src_handle; + list->collection.sorted = false; + return list->cl->insert_iter(iter, elem, 1); +} + +int cxListRemove(CxList *list, size_t index) { + return list->cl->remove(list, index, 1, NULL) == 0; +} + +int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf) { + return list->cl->remove(list, index, 1, targetbuf) == 0; +} + +int cxListRemoveAndGetFirst(CxList *list, void *targetbuf) { + return list->cl->remove(list, 0, 1, targetbuf) == 0; +} + +int cxListRemoveAndGetLast(CxList *list, void *targetbuf) { + // note: index may wrap - member function will catch that + return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0; +} + +size_t cxListRemoveArray(CxList *list, size_t index, size_t num) { + return list->cl->remove(list, index, num, NULL); +} + +size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf) { + return list->cl->remove(list, index, num, targetbuf); +} + +void cxListClear(CxList *list) { + list->cl->clear(list); + list->collection.sorted = true; // empty lists are always sorted +} + +int cxListSwap(CxList *list, size_t i, size_t j) { + list->collection.sorted = false; + return list->cl->swap(list, i, j); +} + +void *cxListAt(const CxList *list, size_t index) { + return list->cl->at(list, index); +} + +void *cxListFirst(const CxList *list) { + return list->cl->at(list, 0); +} + +void *cxListLast(const CxList *list) { + return list->cl->at(list, list->collection.size - 1); +} + +int cxListSet(CxList *list, size_t index, const void *elem) { if (index >= list->collection.size) { return 1; } @@ -515,3 +751,56 @@ int cxListSet( return 0; } + +CxIterator cxListIteratorAt(const CxList *list, size_t index) { + if (list == NULL) list = cxEmptyList; + return list->cl->iterator(list, index, false); +} + +CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index) { + if (list == NULL) list = cxEmptyList; + return list->cl->iterator(list, index, true); +} + +CxIterator cxListIterator(const CxList *list) { + if (list == NULL) list = cxEmptyList; + return list->cl->iterator(list, 0, false); +} + +CxIterator cxListBackwardsIterator(const CxList *list) { + if (list == NULL) list = cxEmptyList; + return list->cl->iterator(list, list->collection.size - 1, true); +} + +size_t cxListFind(const CxList *list, const void *elem) { + return list->cl->find_remove((CxList*)list, elem, false); +} + +bool cxListContains(const CxList* list, const void* elem) { + return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size; +} + +bool cxListIndexValid(const CxList *list, size_t index) { + return index < list->collection.size; +} + +size_t cxListFindRemove(CxList *list, const void *elem) { + return list->cl->find_remove(list, elem, true); +} + +void cxListSort(CxList *list) { + if (list->collection.sorted) return; + list->cl->sort(list); + list->collection.sorted = true; +} + +void cxListReverse(CxList *list) { + // still sorted, but not according to the cmp_func + list->collection.sorted = false; + list->cl->reverse(list); +} + +void cxListFree(CxList *list) { + if (list == NULL) return; + list->cl->deallocate(list); +} diff --git a/ucx/map.c b/ucx/map.c index 49223f5..d0c41f4 100644 --- a/ucx/map.c +++ b/ucx/map.c @@ -51,7 +51,7 @@ static CxMapIterator cx_empty_map_iterator( cx_attr_unused enum cx_map_iterator_type type ) { CxMapIterator iter = {0}; - iter.map.c = map; + iter.map = (CxMap*) map; iter.base.valid = cx_empty_map_iter_valid; return iter; } @@ -84,22 +84,43 @@ CxMap *const cxEmptyMap = &cx_empty_map; // -CxMapIterator cxMapMutIteratorValues(CxMap *map) { - CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); - it.base.mutating = true; - return it; +void cxMapClear(CxMap *map) { + map->cl->clear(map); } -CxMapIterator cxMapMutIteratorKeys(CxMap *map) { - CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); - it.base.mutating = true; - return it; +size_t cxMapSize(const CxMap *map) { + return map->collection.size; } -CxMapIterator cxMapMutIterator(CxMap *map) { - CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); - it.base.mutating = true; - return it; +CxMapIterator cxMapIteratorValues(const CxMap *map) { + if (map == NULL) map = cxEmptyMap; + return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); +} + +CxMapIterator cxMapIteratorKeys(const CxMap *map) { + if (map == NULL) map = cxEmptyMap; + return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); +} + +CxMapIterator cxMapIterator(const CxMap *map) { + if (map == NULL) map = cxEmptyMap; + return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); +} + +int cx_map_put(CxMap *map, CxHashKey key, void *value) { + return map->cl->put(map, key, value) == NULL; +} + +void *cx_map_emplace(CxMap *map, CxHashKey key) { + return map->cl->put(map, key, NULL); +} + +void *cx_map_get(const CxMap *map, CxHashKey key) { + return map->cl->get(map, key); +} + +int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf) { + return map->cl->remove(map, key, targetbuf); } void cxMapFree(CxMap *map) { diff --git a/ucx/mempool.c b/ucx/mempool.c index 8711439..fab6427 100644 --- a/ucx/mempool.c +++ b/ucx/mempool.c @@ -633,13 +633,19 @@ int cxMempoolTransfer( new_source_allocator->data = source; // transfer all the data - memcpy(&dest->data[dest->size], source->data, sizeof(void*)*source->size); - dest->size += source->size; + if (source->size > 0) { + memcpy(&dest->data[dest->size], source->data, + sizeof(void*)*source->size); + dest->size += source->size; + } // transfer all registered memory - memcpy(&dest->registered[dest->registered_size], source->registered, - sizeof(struct cx_mempool_foreign_memory_s) * source->size); - dest->registered_size += source->registered_size; + if (source->registered_size > 0) { + memcpy(&dest->registered[dest->registered_size], source->registered, + sizeof(struct cx_mempool_foreign_memory_s) + * source->registered_size); + dest->registered_size += source->registered_size; + } // register the old allocator with the new pool // we have to remove const-ness for this, but that's okay here diff --git a/ucx/properties.c b/ucx/properties.c index 4593602..3583532 100644 --- a/ucx/properties.c +++ b/ucx/properties.c @@ -51,6 +51,12 @@ void cxPropertiesDestroy(CxProperties *prop) { cxBufferDestroy(&prop->buffer); } +void cxPropertiesReset(CxProperties *prop) { + CxPropertiesConfig config = prop->config; + cxPropertiesDestroy(prop); + cxPropertiesInit(prop, config); +} + int cxPropertiesFilln( CxProperties *prop, const char *buf, @@ -244,7 +250,7 @@ static int cx_properties_sink_map( CxMap *map = sink->sink; CxAllocator *alloc = sink->data; cxmutstr v = cx_strdup_a(alloc, value); - int r = cx_map_put_cxstr(map, key, v.ptr); + int r = cxMapPut(map, key, v.ptr); if (r != 0) cx_strfree_a(alloc, &v); return r; } diff --git a/ucx/string.c b/ucx/string.c index 61d88f3..52ce290 100644 --- a/ucx/string.c +++ b/ucx/string.c @@ -25,6 +25,10 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#ifdef MEMRCHR_NEED_GNU +#define _GNU_SOURCE +#endif + #include "cx/string.h" #include @@ -33,6 +37,7 @@ #include #include #include +#include #ifdef _WIN32 #define cx_strcasecmp_impl _strnicmp @@ -42,7 +47,7 @@ #endif cxmutstr cx_mutstr(char *cstring) { - return (cxmutstr) {cstring, strlen(cstring)}; + return (cxmutstr) {cstring, cstring == NULL ? 0 : strlen(cstring)}; } cxmutstr cx_mutstrn( @@ -53,7 +58,7 @@ cxmutstr cx_mutstrn( } cxstring cx_str(const char *cstring) { - return (cxstring) {cstring, strlen(cstring)}; + return (cxstring) {cstring, cstring == NULL ? 0 : strlen(cstring)}; } cxstring cx_strn( @@ -231,19 +236,24 @@ cxmutstr cx_strchr_m( } cxstring cx_strrchr( - cxstring string, - int chr + cxstring string, + int chr ) { +#ifdef WITH_MEMRCHR + char *ret = memrchr(string.ptr, 0xFF & chr, string.length); + if (ret == NULL) return (cxstring) {NULL, 0}; + return (cxstring) {ret, string.length - (ret - string.ptr)}; +#else chr = 0xFF & chr; size_t i = string.length; while (i > 0) { i--; - // TODO: improve by comparing multiple bytes at once if (string.ptr[i] == chr) { return cx_strsubs(string, i); } } return (cxstring) {NULL, 0}; +#endif } cxmutstr cx_strrchr_m( @@ -451,7 +461,7 @@ size_t cx_strsplit_ma( delim, limit, (cxstring **) output); } -int cx_strcmp( +int cx_strcmp_( cxstring s1, cxstring s2 ) { @@ -468,7 +478,7 @@ int cx_strcmp( } } -int cx_strcasecmp( +int cx_strcasecmp_( cxstring s1, cxstring s2 ) { @@ -520,19 +530,13 @@ cxmutstr cx_strdup_a_( return result; } -static bool str_isspace(char c) { - // TODO: remove once UCX has public API for this - return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f'; -} - cxstring cx_strtrim(cxstring string) { cxstring result = string; - // TODO: optimize by comparing multiple bytes at once - while (result.length > 0 && str_isspace(*result.ptr)) { + while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) { result.ptr++; result.length--; } - while (result.length > 0 && str_isspace(result.ptr[result.length - 1])) { + while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) { result.length--; } return result; @@ -543,7 +547,7 @@ cxmutstr cx_strtrim_m(cxmutstr string) { return (cxmutstr) {(char *) result.ptr, result.length}; } -bool cx_strprefix( +bool cx_strprefix_( cxstring string, cxstring prefix ) { @@ -551,7 +555,7 @@ bool cx_strprefix( return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; } -bool cx_strsuffix( +bool cx_strsuffix_( cxstring string, cxstring suffix ) { @@ -560,7 +564,7 @@ bool cx_strsuffix( suffix.ptr, suffix.length) == 0; } -bool cx_strcaseprefix( +bool cx_strcaseprefix_( cxstring string, cxstring prefix ) { @@ -572,7 +576,7 @@ bool cx_strcaseprefix( #endif } -bool cx_strcasesuffix( +bool cx_strcasesuffix_( cxstring string, cxstring suffix ) { @@ -957,11 +961,6 @@ int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep return 0; } -static bool str_isdigit(char c) { - // TODO: remove once UCX has public API for this - return c >= '0' && c <= '9'; -} - int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) { // TODO: overflow check // TODO: increase precision @@ -994,7 +993,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse // parse all digits until we find the decsep size_t pos = 0; do { - if (str_isdigit(str.ptr[pos])) { + if (isdigit((unsigned char)str.ptr[pos])) { result = result * 10 + (str.ptr[pos] - '0'); } else if (strchr(groupsep, str.ptr[pos]) == NULL) { break; @@ -1023,7 +1022,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse // parse everything until exponent or end double factor = 1.; do { - if (str_isdigit(str.ptr[pos])) { + if (isdigit((unsigned char)str.ptr[pos])) { factor *= 0.1; result = result + factor * (str.ptr[pos] - '0'); } else if (strchr(groupsep, str.ptr[pos]) == NULL) { @@ -1064,7 +1063,7 @@ int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupse // parse the exponent unsigned int exp = 0; do { - if (str_isdigit(str.ptr[pos])) { + if (isdigit((unsigned char)str.ptr[pos])) { exp = 10 * exp + (str.ptr[pos] - '0'); } else if (strchr(groupsep, str.ptr[pos]) == NULL) { errno = EINVAL; diff --git a/ucx/tree.c b/ucx/tree.c index 1b2b38e..d948a4c 100644 --- a/ucx/tree.c +++ b/ucx/tree.c @@ -375,7 +375,7 @@ CxTreeIterator cx_tree_iterator( iter.skip = false; // assign base iterator functions - iter.base.mutating = false; + iter.base.allow_remove = false; iter.base.remove = false; iter.base.current_impl = NULL; iter.base.valid = cx_tree_iter_valid; @@ -496,7 +496,7 @@ CxTreeVisitor cx_tree_visitor( iter.queue_last = NULL; // assign base iterator functions - iter.base.mutating = false; + iter.base.allow_remove = false; iter.base.remove = false; iter.base.current_impl = NULL; iter.base.valid = cx_tree_visitor_valid; @@ -717,7 +717,7 @@ size_t cx_tree_add_array( } // otherwise, create iterator and hand over to other function - CxIterator iter = cxIterator(src, elem_size, num); + CxIterator iter = cxIterator(src, elem_size, num, false); return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc, cfunc, cdata, failed, root, loc_parent, loc_children, loc_last_child, @@ -804,16 +804,12 @@ static cx_tree_class cx_tree_default_class = { cx_tree_default_find }; -CxTree *cxTreeCreate( - const CxAllocator *allocator, +CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func, cx_tree_search_func search_func, cx_tree_search_data_func search_data_func, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next ) { if (allocator == NULL) { allocator = cxDefaultAllocator; @@ -852,15 +848,9 @@ void cxTreeFree(CxTree *tree) { cxFree(tree->allocator, tree); } -CxTree *cxTreeCreateWrapped( - const CxAllocator *allocator, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -) { +CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next) { if (allocator == NULL) { allocator = cxDefaultAllocator; } @@ -888,11 +878,7 @@ CxTree *cxTreeCreateWrapped( return tree; } -void cxTreeSetParent( - CxTree *tree, - void *parent, - void *child -) { +void cxTreeSetParent(CxTree *tree, void *parent, void *child) { size_t loc_parent = tree->loc_parent; if (tree_parent(child) == NULL) { tree->size++; @@ -900,19 +886,12 @@ void cxTreeSetParent( cx_tree_link(parent, child, cx_tree_node_layout(tree)); } -void cxTreeAddChildNode( - CxTree *tree, - void *parent, - void *child -) { +void cxTreeAddChildNode(CxTree *tree, void *parent, void *child) { cx_tree_link(parent, child, cx_tree_node_layout(tree)); tree->size++; } -int cxTreeAddChild( - CxTree *tree, - void *parent, - const void *data) { +int cxTreeAddChild(CxTree *tree, void *parent, const void *data) { void *node = tree->node_create(data, tree); if (node == NULL) return 1; cx_tree_zero_pointers(node, cx_tree_node_layout(tree)); @@ -921,6 +900,29 @@ int cxTreeAddChild( return 0; } +int cxTreeInsert(CxTree *tree, const void *data) { + return tree->cl->insert_element(tree, data); +} + +size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n) { + return tree->cl->insert_many(tree, iter, n); +} + +size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n) { + if (n == 0) return 0; + if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0; + CxIterator iter = cxIterator(data, elem_size, n, false); + return cxTreeInsertIter(tree, cxIteratorRef(iter), n); +} + +void *cxTreeFind( CxTree *tree, const void *data) { + return tree->cl->find(tree, tree->root, data, 0); +} + +void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth) { + return tree->cl->find(tree, subtree_root, data, max_depth); +} + size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root) { CxTreeVisitor visitor = cx_tree_visitor( subtree_root, @@ -945,6 +947,10 @@ size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root) { return visitor.depth; } +size_t cxTreeSize(CxTree *tree) { + return tree->size; +} + size_t cxTreeDepth(CxTree *tree) { CxTreeVisitor visitor = cx_tree_visitor( tree->root, tree->loc_children, tree->loc_next @@ -1052,3 +1058,38 @@ void cxTreeDestroySubtree(CxTree *tree, void *node) { tree->root = NULL; } } + +void cxTreeIteratorDispose(CxTreeIterator *iter) { + cxFreeDefault(iter->stack); + iter->stack = NULL; +} + +void cxTreeVisitorDispose(CxTreeVisitor *visitor) { + struct cx_tree_visitor_queue_s *q = visitor->queue_next; + while (q != NULL) { + struct cx_tree_visitor_queue_s *next = q->next; + cxFreeDefault(q); + q = next; + } +} + +CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit) { + return cx_tree_iterator( + node, visit_on_exit, + tree->loc_children, tree->loc_next + ); +} + +CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) { + return cx_tree_visitor( + node, tree->loc_children, tree->loc_next + ); +} + +CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit) { + return cxTreeIterateSubtree(tree, tree->root, visit_on_exit); +} + +CxTreeVisitor cxTreeVisit(CxTree *tree) { + return cxTreeVisitSubtree(tree, tree->root); +} diff --git a/ui/common/args.c b/ui/common/args.c index e799889..796919b 100644 --- a/ui/common/args.c +++ b/ui/common/args.c @@ -33,6 +33,156 @@ #include "../ui/container.h" + +/* ---------------------------- UiDialogArgs ---------------------------- */ + +UiDialogArgs* ui_dialog_args_new(void) { + UiDialogArgs *args = malloc(sizeof(UiDialogArgs)); + memset(args, 0, sizeof(UiDialogArgs)); + return args; +} + +void ui_dialog_args_set_title(UiDialogArgs *args, const char *title) { + args->title = strdup(title); +} + +void ui_dialog_args_set_content(UiDialogArgs *args, const char *str) { + args->content = strdup(str); +} + +void ui_dialog_args_set_button1_label(UiDialogArgs *args, const char *label) { + args->button1_label = strdup(label); +} + +void ui_dialog_args_set_button2_label(UiDialogArgs *args, const char *label) { + args->button2_label = strdup(label); +} + +void ui_dialog_args_set_closebutton_label(UiDialogArgs *args, const char *label) { + args->closebutton_label = strdup(label); +} + +void ui_dialog_args_set_input_value(UiDialogArgs *args, const char *value) { + args->input_value = strdup(value); +} + +void ui_dialog_args_set_input(UiDialogArgs *args, UiBool input) { + args->input = input; +} + +void ui_dialog_args_set_password(UiDialogArgs *args, UiBool password) { + args->password = password; +} + +void ui_dialog_args_set_result(UiDialogArgs *args, ui_callback cb) { + args->result = cb; +} + +void ui_dialog_args_set_resultdata(UiDialogArgs *args, void *userdata) { + args->resultdata = userdata; +} + +void ui_dialog_args_free(UiDialogArgs *args) { + free((void*)args->title); + free((void*)args->button1_label); + free((void*)args->button2_label); + free((void*)args->content); + free((void*)args->closebutton_label); + free((void*)args->input_value); + free(args); +} + + +/* -------------------------- UiDialogWindowArgs -------------------------- */ + +UiDialogWindowArgs* ui_dialogwindow_args_new(void) { + UiDialogWindowArgs *args = malloc(sizeof(UiDialogWindowArgs)); + memset(args, 0, sizeof(UiDialogWindowArgs)); + return args; +} + +void ui_dialogwindow_args_set_modal(UiDialogWindowArgs *args, UiTri value) { + args->modal = value; +} + +void ui_dialogwindow_args_set_titlebar_buttons(UiDialogWindowArgs *args, UiTri value) { + args->titlebar_buttons = value; +} + +void ui_dialogwindow_args_set_show_closebutton(UiDialogWindowArgs *args, UiTri value) { + args->show_closebutton = value; +} + +void ui_dialogwindow_args_set_title(UiDialogWindowArgs *args, const char *title) { + args->title = strdup(title); +} + +void ui_dialogwindow_args_set_lbutton1(UiDialogWindowArgs *args, const char *label) { + args->lbutton1 = strdup(label); +} + +void ui_dialogwindow_args_set_lbutton2(UiDialogWindowArgs *args, const char *label) { + args->lbutton2 = strdup(label); +} + +void ui_dialogwindow_args_set_rbutton3(UiDialogWindowArgs *args, const char *label) { + args->rbutton3 = strdup(label); +} + +void ui_dialogwindow_args_set_rbutton4(UiDialogWindowArgs *args, const char *label) { + args->rbutton4 = strdup(label); +} + +void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states) { + // TODO +} + +void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states) { + // TODO +} + +void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states) { + // TODO +} + +void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states) { + // TODO +} + +void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button) { + args->default_button = button; +} + +void ui_dialogwindow_args_set_width(UiDialogWindowArgs *args, int width) { + args->width = width; +} + +void ui_dialogwindow_args_set_height(UiDialogWindowArgs *args, int height) { + args->height = height; +} + +void ui_dialogwindow_args_set_onclick(UiDialogWindowArgs *args, ui_callback cb) { + args->onclick = cb; +} + +void ui_dialogwindow_args_set_onclickdata(UiDialogWindowArgs *args, void *userdata) { + args->onclickdata = userdata; +} + +void ui_dialogwindow_args_free(UiDialogWindowArgs *args) { + free((void*)args->title); + free((void*)args->lbutton1); + free((void*)args->lbutton2); + free((void*)args->rbutton3); + free((void*)args->rbutton4); + free((void*)args->lbutton1_groups); + free((void*)args->lbutton2_groups); + free((void*)args->rbutton3_groups); + free((void*)args->rbutton4_groups); + free(args); +} + + /* ---------------------------- UiMenuItemArgs ---------------------------- */ UiMenuItemArgs* ui_menuitem_args_new(void) { @@ -45,10 +195,6 @@ void ui_menuitem_args_set_label(UiMenuItemArgs *args, const char *label) { args->label = strdup(label); } -void ui_menuitem_args_set_stockid(UiMenuItemArgs *args, const char *stockid) { - args->stockid = strdup(stockid); -} - void ui_menuitem_args_set_icon(UiMenuItemArgs *args, const char *icon) { args->icon = strdup(icon); } @@ -63,7 +209,6 @@ void ui_menuitem_args_set_onclickdata(UiMenuItemArgs *args, void *onclickdata) { void ui_menuitem_args_free(UiMenuItemArgs *args) { free((void*)args->label); - free((void*)args->stockid); free((void*)args->icon); free(args); } @@ -81,10 +226,6 @@ void ui_menutoggleitem_args_set_label(UiMenuToggleItemArgs *args, const char *la args->label = strdup(label); } -void ui_menutoggleitem_args_set_stockid(UiMenuToggleItemArgs *args, const char *stockid) { - args->stockid = strdup(stockid); -} - void ui_menutoggleitem_args_set_icon(UiMenuToggleItemArgs *args, const char *icon) { args->icon = strdup(icon); } @@ -103,7 +244,6 @@ void ui_menutoggleitem_args_set_onchangedata(UiMenuToggleItemArgs *args, void *o void ui_menutoggleitem_args_free(UiMenuToggleItemArgs *args) { free((void*)args->label); - free((void*)args->stockid); free((void*)args->icon); free((void*)args->varname); free(args); @@ -154,14 +294,14 @@ void ui_toolbar_item_args_set_label(UiToolbarItemArgs *args, const char *label) args->label = strdup(label); } -void ui_toolbar_item_args_set_stockid(UiToolbarItemArgs *args, const char *stockid) { - args->stockid = strdup(stockid); -} - void ui_toolbar_item_args_set_icon(UiToolbarItemArgs *args, const char *icon) { args->icon = strdup(icon); } +void ui_toolbar_item_args_set_tooltip(UiToolbarItemArgs *args, const char *tooltip) { + args->tooltip = strdup(tooltip); +} + void ui_toolbar_item_args_set_onclick(UiToolbarItemArgs *args, ui_callback callback) { args->onclick = callback; } @@ -170,13 +310,16 @@ void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclick args->onclickdata = onclickdata; } -void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *groups) { - // TODO +void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; } void ui_toolbar_item_args_free(UiToolbarItemArgs *args) { free((void*)args->label); - free((void*)args->stockid); free((void*)args->icon); + free((void*)args->tooltip); + free((void*)args->groups); free(args); } @@ -188,47 +331,42 @@ UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void) { return args; } - void ui_toolbar_toggleitem_args_set_label(UiToolbarToggleItemArgs *args, const char *label) { args->label = strdup(label); } - -void ui_toolbar_toggleitem_args_set_stockid(UiToolbarToggleItemArgs *args, const char *stockid) { - args->stockid = strdup(stockid); -} - - void ui_toolbar_toggleitem_args_set_icon(UiToolbarToggleItemArgs *args, const char *icon) { args->icon = strdup(icon); } +void ui_toolbar_toggleitem_args_set_tooltip(UiToolbarToggleItemArgs *args, const char *tooltip) { + args->tooltip = strdup(tooltip); +} void ui_toolbar_toggleitem_args_set_varname(UiToolbarToggleItemArgs *args, const char *varname) { args->varname = strdup(varname); } - void ui_toolbar_toggleitem_args_set_onchange(UiToolbarToggleItemArgs *args, ui_callback callback) { args->onchange = callback; } - void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, void *onchangedata) { args->onchangedata = onchangedata; } - -void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *groups) { - // TODO +void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args,int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; } - void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args) { free((void*)args->label); - free((void*)args->stockid); free((void*)args->icon); + free((void*)args->tooltip); free((void*)args->varname); + free((void*)args->groups); free(args); } @@ -241,26 +379,22 @@ UiToolbarMenuArgs* ui_toolbar_menu_args_new(void) { return args; } - void ui_toolbar_menu_args_set_label(UiToolbarMenuArgs *args, const char *label) { args->label = strdup(label); } - -void ui_toolbar_menu_args_set_stockid(UiToolbarMenuArgs *args, const char *stockid) { - args->stockid = strdup(stockid); -} - - void ui_toolbar_menu_args_set_icon(UiToolbarMenuArgs *args, const char *icon) { args->icon = strdup(icon); } +void ui_toolbar_menu_args_set_tooltip(UiToolbarMenuArgs *args, const char *tooltip) { + args->tooltip = strdup(tooltip); +} void ui_toolbar_menu_args_free(UiToolbarMenuArgs *args) { free((void*)args->label); - free((void*)args->stockid); free((void*)args->icon); + free((void*)args->tooltip); free(args); } @@ -274,7 +408,7 @@ UiContainerArgs* ui_container_args_new(void) { } void ui_container_args_set_fill(UiContainerArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } void ui_container_args_set_hexpand(UiContainerArgs *args, UiBool value) { @@ -346,6 +480,22 @@ void ui_container_args_set_margin(UiContainerArgs *args, int value) { args->margin = value; } +void ui_container_args_set_margin_left(UiContainerArgs *args, int value) { + args->margin_left = value; +} + +void ui_container_args_set_margin_right(UiContainerArgs *args, int value) { + args->margin_right = value; +} + +void ui_container_args_set_margin_top(UiContainerArgs *args, int value) { + args->margin_top = value; +} + +void ui_container_args_set_margin_bottom(UiContainerArgs *args, int value) { + args->margin_bottom = value; +} + void ui_container_args_set_spacing(UiContainerArgs *args, int value) { args->spacing = value; @@ -379,7 +529,7 @@ UiFrameArgs* ui_frame_args_new(void) { void ui_frame_args_set_fill(UiFrameArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } @@ -407,6 +557,26 @@ void ui_frame_args_set_override_defaults(UiFrameArgs *args, UiBool value) { args->override_defaults = value; } +void ui_frame_args_set_margin(UiFrameArgs *args, int value) { + args->margin = value; +} + +void ui_frame_args_set_margin_left(UiFrameArgs *args, int value) { + args->margin_left = value; +} + +void ui_frame_args_set_margin_right(UiFrameArgs *args, int value) { + args->margin_right = value; +} + +void ui_frame_args_set_margin_top(UiFrameArgs *args, int value) { + args->margin_top = value; +} + +void ui_frame_args_set_margin_bottom(UiFrameArgs *args, int value) { + args->margin_bottom = value; +} + void ui_frame_args_set_colspan(UiFrameArgs *args, int colspan) { args->colspan = colspan; @@ -427,11 +597,13 @@ void ui_frame_args_set_style_class(UiFrameArgs *args, const char *classname) { args->style_class = strdup(classname); } - -void ui_frame_args_set_margin(UiFrameArgs *args, int value) { - args->margin = value; +void ui_frame_args_set_subcontainer(UiFrameArgs *args, UiSubContainerType subcontainer) { + args->subcontainer = subcontainer; } +void ui_frame_args_set_padding(UiFrameArgs *args, int value) { + args->padding = value; +} void ui_frame_args_set_spacing(UiFrameArgs *args, int value) { args->spacing = value; @@ -457,7 +629,6 @@ void ui_frame_args_set_label(UiFrameArgs *args, const char *label) { args->label = strdup(label); } - void ui_frame_args_free(UiFrameArgs *args) { free((void*)args->name); free((void*)args->style_class); @@ -474,27 +645,38 @@ UiSidebarArgs* ui_sidebar_args_new(void) { return args; } - void ui_sidebar_args_set_name(UiSidebarArgs *args, const char *name) { args->name = strdup(name); } - void ui_sidebar_args_set_style_class(UiSidebarArgs *args, const char *classname) { args->style_class = strdup(classname); } - void ui_sidebar_args_set_margin(UiSidebarArgs *args, int value) { args->margin = value; } +void ui_sidebar_args_set_margin_left(UiSidebarArgs *args, int value) { + args->margin_left = value; +} + +void ui_sidebar_args_set_margin_right(UiSidebarArgs *args, int value) { + args->margin_right = value; +} + +void ui_sidebar_args_set_margin_top(UiSidebarArgs *args, int value) { + args->margin_top = value; +} + +void ui_sidebar_args_set_margin_bottom(UiSidebarArgs *args, int value) { + args->margin_bottom = value; +} void ui_sidebar_args_set_spacing(UiSidebarArgs *args, int value) { args->spacing = value; } - void ui_sidebar_args_free(UiSidebarArgs *args) { free((void*)args->name); free((void*)args->style_class); @@ -510,61 +692,66 @@ UiSplitPaneArgs* ui_splitpane_args_new(void) { return args; } - void ui_splitpane_args_set_fill(UiSplitPaneArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } - void ui_splitpane_args_set_hexpand(UiSplitPaneArgs *args, UiBool value) { args->hexpand = value; } - void ui_splitpane_args_set_vexpand(UiSplitPaneArgs *args, UiBool value) { args->vexpand = value; } - void ui_splitpane_args_set_hfill(UiSplitPaneArgs *args, UiBool value) { args->hfill = value; } - void ui_splitpane_args_set_vfill(UiSplitPaneArgs *args, UiBool value) { args->vfill = value; } - void ui_splitpane_args_set_override_defaults(UiSplitPaneArgs *args, UiBool value) { args->override_defaults = value; } - void ui_splitpane_args_set_colspan(UiSplitPaneArgs *args, int colspan) { args->colspan = colspan; } - void ui_splitpane_args_set_rowspan(UiSplitPaneArgs *args, int rowspan) { args->rowspan = rowspan; } - void ui_splitpane_args_set_name(UiSplitPaneArgs *args, const char *name) { args->name = strdup(name); } - void ui_splitpane_args_set_style_class(UiSplitPaneArgs *args, const char *classname) { args->style_class = strdup(classname); } - void ui_splitpane_args_set_margin(UiSplitPaneArgs *args, int value) { args->margin = value; } +void ui_splitpane_args_set_margin_left(UiSplitPaneArgs *args, int value) { + args->margin_left = value; +} + +void ui_splitpane_args_set_margin_right(UiSplitPaneArgs *args, int value) { + args->margin_right = value; +} + +void ui_splitpane_args_set_margin_top(UiSplitPaneArgs *args, int value) { + args->margin_top = value; +} + +void ui_splitpane_args_set_margin_bottom(UiSplitPaneArgs *args, int value) { + args->margin_bottom = value; +} + void ui_splitpane_args_set_spacing(UiSplitPaneArgs *args, int value) { args->spacing = value; @@ -585,6 +772,9 @@ void ui_splitpane_args_set_initial_position(UiSplitPaneArgs *args, int pos) { args->initial_position = pos; } +void ui_splitpane_args_set_position_property(UiSplitPaneArgs *args, const char *propname) { + args->position_property = strdup(propname); +} void ui_splitpane_args_set_varname(UiSplitPaneArgs *args, const char *varname) { args->varname = strdup(varname); @@ -599,8 +789,125 @@ void ui_splitpane_args_set_max_panes(UiSplitPaneArgs *args, int max) { args->max_panes = max; } - void ui_splitpane_args_free(UiSplitPaneArgs *args) { + free((void*)args->name); + free((void*)args->style_class); + free((void*)args->varname); + free((void*)args->position_property); + free(args); +} + + +/* ---------------------------- UiTabViewArgs ---------------------------- */ + +UiTabViewArgs* ui_tabview_args_new(void) { + UiTabViewArgs *args = malloc(sizeof(UiTabViewArgs)); + memset(args, 0, sizeof(UiTabViewArgs)); + return args; +} + +void ui_tabview_args_set_fill(UiTabViewArgs *args, UiBool fill) { + args->fill = fill; +} + +void ui_tabview_args_set_hexpand(UiTabViewArgs *args, UiBool value) { + args->hexpand = value; +} + +void ui_tabview_args_set_vexpand(UiTabViewArgs *args, UiBool value) { + args->vexpand = value; +} + +void ui_tabview_args_set_hfill(UiTabViewArgs *args, UiBool value) { + args->hfill = value; +} + +void ui_tabview_args_set_vfill(UiTabViewArgs *args, UiBool value) { + args->vfill = value; +} + +void ui_tabview_args_set_override_defaults(UiTabViewArgs *args, UiBool value) { + args->override_defaults = value; +} + +void ui_tabview_args_set_colspan(UiTabViewArgs *args, int colspan) { + args->colspan = colspan; +} + +void ui_tabview_args_set_rowspan(UiTabViewArgs *args, int rowspan) { + args->rowspan = rowspan; +} + +void ui_tabview_args_set_name(UiTabViewArgs *args, const char *name) { + args->name = strdup(name); +} + +void ui_tabview_args_set_style_class(UiTabViewArgs *args, const char *classname) { + args->style_class = strdup(classname); +} + +void ui_tabview_args_set_margin(UiTabViewArgs *args, int value) { + args->margin = value; +} + +void ui_tabview_args_set_margin_left(UiTabViewArgs *args, int value) { + args->margin_left = value; +} + +void ui_tabview_args_set_margin_right(UiTabViewArgs *args, int value) { + args->margin_right = value; +} + +void ui_tabview_args_set_margin_top(UiTabViewArgs *args, int value) { + args->margin_top = value; +} + +void ui_tabview_args_set_margin_bottom(UiTabViewArgs *args, int value) { + args->margin_bottom = value; +} + +void ui_tabview_args_set_padding(UiTabViewArgs *args, int value) { + args->padding = value; +} + +void ui_tabview_args_set_spacing(UiTabViewArgs *args, int value) { + args->spacing = value; +} + +void ui_tabview_args_set_columnspacing(UiTabViewArgs *args, int value) { + args->columnspacing = value; +} + + +void ui_tabview_args_set_rowspacing(UiTabViewArgs *args, int value) { + args->rowspacing = value; +} + +void ui_tabview_args_set_type(UiTabViewArgs *args, UiTabViewType tabview) { + args->tabview = tabview; +} + +void ui_tabview_args_set_onchange(UiTabViewArgs *args, ui_callback cb) { + args->onchange = cb; +} + +void ui_tabview_args_set_onchangedata(UiTabViewArgs *args, void *userdata) { + args->onchangedata = userdata; +} + +void ui_tabview_args_set_varname(UiTabViewArgs *args, const char *varname) { + args->varname = strdup(varname); +} + +void ui_tabview_args_set_value(UiTabViewArgs *args, UiInteger *value) { + args->value = value; +} + +void ui_tabview_args_set_subcontainer(UiTabViewArgs *args, UiSubContainerType subcontainer) { + args->subcontainer = subcontainer; +} + +void ui_tabview_args_free(UiTabViewArgs *args) { free((void*)args->name); free((void*)args->style_class); free((void*)args->varname); @@ -608,6 +915,90 @@ void ui_splitpane_args_free(UiSplitPaneArgs *args) { } +/* ------------------------- UiWidgetArgs ----------------------------*/ + +UiWidgetArgs* ui_widget_args_new(void) { + UiWidgetArgs *args = malloc(sizeof(UiWidgetArgs)); + memset(args, 0, sizeof(UiWidgetArgs)); + return args; +} + + +void ui_widget_args_set_fill(UiWidgetArgs *args, UiBool fill) { + args->fill = fill; +} + + +void ui_widget_args_set_hexpand(UiWidgetArgs *args, UiBool value) { + args->hexpand = value; +} + + +void ui_widget_args_set_vexpand(UiWidgetArgs *args, UiBool value) { + args->vexpand = value; +} + + +void ui_widget_args_set_hfill(UiWidgetArgs *args, UiBool value) { + args->hfill = value; +} + + +void ui_widget_args_set_vfill(UiWidgetArgs *args, UiBool value) { + args->vfill = value; +} + + +void ui_widget_args_set_override_defaults(UiWidgetArgs *args, UiBool value) { + args->override_defaults = value; +} + +void ui_widget_args_set_margin(UiWidgetArgs *args, int value) { + args->margin = value; +} + +void ui_widget_args_set_margin_left(UiWidgetArgs *args, int value) { + args->margin_left = value; +} + +void ui_widget_args_set_margin_right(UiWidgetArgs *args, int value) { + args->margin_right = value; +} + +void ui_widget_args_set_margin_top(UiWidgetArgs *args, int value) { + args->margin_top = value; +} + +void ui_widget_args_set_margin_bottom(UiWidgetArgs *args, int value) { + args->margin_bottom = value; +} + +void ui_widget_args_set_colspan(UiWidgetArgs *args, int colspan) { + args->colspan = colspan; +} + + +void ui_widget_args_set_rowspan(UiWidgetArgs *args, int rowspan) { + args->rowspan = rowspan; +} + + +void ui_widget_args_set_name(UiWidgetArgs *args, const char *name) { + args->name = strdup(name); +} + + +void ui_widget_args_set_style_class(UiWidgetArgs *args, const char *classname) { + args->style_class = strdup(classname); +} + +void ui_widget_args_free(UiWidgetArgs *args) { + free((void*)args->name); + free((void*)args->style_class); + free(args); +} + + /* ------------------------- UiLabelArgs ----------------------------*/ @@ -617,36 +1008,49 @@ UiLabelArgs* ui_label_args_new(void) { return args; } - void ui_label_args_set_fill(UiLabelArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } - void ui_label_args_set_hexpand(UiLabelArgs *args, UiBool value) { args->hexpand = value; } - void ui_label_args_set_vexpand(UiLabelArgs *args, UiBool value) { args->vexpand = value; } - void ui_label_args_set_hfill(UiLabelArgs *args, UiBool value) { args->hfill = value; } - void ui_label_args_set_vfill(UiLabelArgs *args, UiBool value) { args->vfill = value; } - void ui_label_args_set_override_defaults(UiLabelArgs *args, UiBool value) { args->override_defaults = value; } +void ui_label_args_set_margin(UiLabelArgs *args, int value) { + args->margin = value; +} + +void ui_label_args_set_margin_left(UiLabelArgs *args, int value) { + args->margin_left = value; +} + +void ui_label_args_set_margin_right(UiLabelArgs *args, int value){ + args->margin_right = value; +} + +void ui_label_args_set_margin_top(UiLabelArgs *args, int value) { + args->margin_top = value; +} + +void ui_label_args_set_margin_bottom(UiLabelArgs *args, int value) { + args->margin_bottom = value; +} void ui_label_args_set_colspan(UiLabelArgs *args, int colspan) { args->colspan = colspan; @@ -657,12 +1061,10 @@ void ui_label_args_set_rowspan(UiLabelArgs *args, int rowspan) { args->rowspan = rowspan; } - void ui_label_args_set_name(UiLabelArgs *args, const char *name) { args->name = strdup(name); } - void ui_label_args_set_style_class(UiLabelArgs *args, const char *classname) { args->style_class = strdup(classname); } @@ -671,7 +1073,6 @@ void ui_label_args_set_label(UiLabelArgs *args, const char *label){ args->label = strdup(label); } - void ui_label_args_set_align(UiLabelArgs *args, UiAlignment align) { args->align = align; } @@ -708,7 +1109,7 @@ UiProgressbarArgs* ui_progressbar_args_new(void) { void ui_progressbar_args_set_fill(UiProgressbarArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } @@ -736,6 +1137,25 @@ void ui_progressbar_args_set_override_defaults(UiProgressbarArgs *args, UiBool v args->override_defaults = value; } +void ui_progressbar_args_set_margin(UiProgressbarArgs *args, int value) { + args->margin = value; +} + +void ui_progressbar_args_set_margin_left(UiProgressbarArgs *args, int value) { + args->margin_left = value; +} + +void ui_progressbar_args_set_margin_right(UiProgressbarArgs *args, int value) { + args->margin_right = value; +} + +void ui_progressbar_args_set_margin_top(UiProgressbarArgs *args, int value) { + args->margin_top = value; +} + +void ui_progressbar_args_set_margin_bottom(UiProgressbarArgs *args, int value) { + args->margin_bottom = value; +} void ui_progressbar_args_set_colspan(UiProgressbarArgs *args, int colspan) { args->colspan = colspan; @@ -789,7 +1209,7 @@ UiProgressbarSpinnerArgs* ui_progress_spinner_args_new(void) { } void ui_progress_spinner_args_set_fill(UiProgressbarSpinnerArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } void ui_progress_spinner_args_set_hexpand(UiProgressbarSpinnerArgs *args, UiBool value) { @@ -812,6 +1232,26 @@ void ui_progress_spinner_args_set_override_defaults(UiProgressbarSpinnerArgs *ar args->override_defaults = value; } +void ui_progress_spinner_args_set_margin(UiProgressbarSpinnerArgs *args, int value) { + args->margin = value; +} + +void ui_progress_spinner_args_set_margin_left(UiProgressbarSpinnerArgs *args, int value) { + args->margin_left = value; +} + +void ui_progress_spinner_args_set_margin_right(UiProgressbarSpinnerArgs *args, int value) { + args->margin_right = value; +} + +void ui_progress_spinner_args_set_margin_top(UiProgressbarSpinnerArgs *args, int value) { + args->margin_top = value; +} + +void ui_progress_spinner_args_set_margin_bottom(UiProgressbarSpinnerArgs *args, int value) { + args->margin_bottom = value; +} + void ui_progress_spinner_args_set_colspan(UiProgressbarSpinnerArgs *args, int colspan) { args->colspan = colspan; } @@ -854,7 +1294,7 @@ UiButtonArgs* ui_button_args_new(void) { void ui_button_args_set_fill(UiButtonArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } @@ -868,192 +1308,342 @@ void ui_button_args_set_vexpand(UiButtonArgs *args, UiBool value) { } -void ui_button_args_set_hfill(UiButtonArgs *args, UiBool value) { - args->hfill = value; +void ui_button_args_set_hfill(UiButtonArgs *args, UiBool value) { + args->hfill = value; +} + + +void ui_button_args_set_vfill(UiButtonArgs *args, UiBool value) { + args->vfill = value; +} + + +void ui_button_args_set_override_defaults(UiButtonArgs *args, UiBool value) { + args->override_defaults = value; +} + +void ui_button_args_set_margin(UiButtonArgs *args, int value) { + args->margin = value; +} + +void ui_button_args_set_margin_left(UiButtonArgs *args, int value) { + args->margin_left = value; +} + +void ui_button_args_set_margin_right(UiButtonArgs *args, int value) { + args->margin_right = value; +} + +void ui_button_args_set_margin_top(UiButtonArgs *args, int value) { + args->margin_top = value; +} + +void ui_button_args_set_margin_bottom(UiButtonArgs *args, int value) { + args->margin_bottom = value; +} + +void ui_button_args_set_colspan(UiButtonArgs *args, int colspan) { + args->colspan = colspan; +} + + +void ui_button_args_set_rowspan(UiButtonArgs *args, int rowspan) { + args->rowspan = rowspan; +} + +void ui_button_args_set_name(UiButtonArgs *args, const char *name) { + args->name = strdup(name); +} + +void ui_button_args_set_style_class(UiButtonArgs *args, const char *classname) { + args->style_class = strdup(classname); +} + +void ui_button_args_set_label(UiButtonArgs *args, const char *label){ + args->label = strdup(label); +} + +void ui_button_args_set_icon(UiButtonArgs *args, const char *icon){ + args->icon = strdup(icon); +} + +void ui_button_args_set_tooltip(UiButtonArgs *args, const char *tooltip) { + args->tooltip = strdup(tooltip); +} + +void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype){ + args->labeltype = labeltype; +} + +void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback){ + args->onclick = callback; +} + +void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata){ + args->onclickdata = onclickdata; +} + +void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; +} + +void ui_button_args_free(UiButtonArgs *args) { + free((void*)args->name); + free((void*)args->style_class); + free((void*)args->label); + free((void*)args->icon); + free((void*)args->tooltip); + free((void*)args->groups); + free(args); +} + + +/* ------------------------- UiToggleArgs ----------------------------*/ + + +UiToggleArgs* ui_toggle_args_new(void) { + UiToggleArgs *args = malloc(sizeof(UiToggleArgs)); + memset(args, 0, sizeof(UiToggleArgs)); + return args; +} + +void ui_toggle_args_set_fill(UiToggleArgs *args, UiBool fill) { + args->fill = fill; +} + +void ui_toggle_args_set_hexpand(UiToggleArgs *args, UiBool value) { + args->hexpand = value; +} + +void ui_toggle_args_set_vexpand(UiToggleArgs *args, UiBool value) { + args->vexpand = value; +} + +void ui_toggle_args_set_hfill(UiToggleArgs *args, UiBool value) { + args->hfill = value; +} + +void ui_toggle_args_set_vfill(UiToggleArgs *args, UiBool value) { + args->vfill = value; +} + +void ui_toggle_args_set_override_defaults(UiToggleArgs *args, UiBool value) { + args->override_defaults = value; } +void ui_toggle_args_set_margin(UiToggleArgs *args, int value) { + args->margin = value; +} -void ui_button_args_set_vfill(UiButtonArgs *args, UiBool value) { - args->vfill = value; +void ui_toggle_args_set_margin_left(UiToggleArgs *args, int value) { + args->margin_left = value; } +void ui_toggle_args_set_margin_right(UiToggleArgs *args, int value) { + args->margin_right = value; +} -void ui_button_args_set_override_defaults(UiButtonArgs *args, UiBool value) { - args->override_defaults = value; +void ui_toggle_args_set_margin_top(UiToggleArgs *args, int value) { + args->margin_top = value; } +void ui_toggle_args_set_margin_bottom(UiToggleArgs *args, int value) { + args->margin_bottom = value; +} -void ui_button_args_set_colspan(UiButtonArgs *args, int colspan) { +void ui_toggle_args_set_colspan(UiToggleArgs *args, int colspan) { args->colspan = colspan; } - -void ui_button_args_set_rowspan(UiButtonArgs *args, int rowspan) { +void ui_toggle_args_set_rowspan(UiToggleArgs *args, int rowspan) { args->rowspan = rowspan; } -void ui_button_args_set_name(UiButtonArgs *args, const char *name) { +void ui_toggle_args_set_name(UiToggleArgs *args, const char *name) { args->name = strdup(name); } - -void ui_button_args_set_style_class(UiButtonArgs *args, const char *classname) { +void ui_toggle_args_set_style_class(UiToggleArgs *args, const char *classname) { args->style_class = strdup(classname); } -void ui_button_args_set_label(UiButtonArgs *args, const char *label){ +void ui_toggle_args_set_label(UiToggleArgs *args, const char *label){ args->label = strdup(label); } - -void ui_button_args_set_stockid(UiButtonArgs *args, const char *stockid){ - args->stockid = strdup(stockid); +void ui_toggle_args_set_icon(UiToggleArgs *args, const char *icon){ + args->icon = strdup(icon); } +void ui_toggle_args_set_tooltip(UiToggleArgs *args, const char *tooltip) { + args->tooltip = strdup(tooltip); +} -void ui_button_args_set_icon(UiButtonArgs *args, const char *icon){ - args->icon = strdup(icon); +void ui_toggle_args_set_labeltype(UiToggleArgs *args, int labeltype){ + args->labeltype = labeltype; } +void ui_toggle_args_set_onchange(UiToggleArgs *args, ui_callback callback){ + args->onchange = callback; +} -void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype){ - args->labeltype = labeltype; +void ui_toggle_args_set_onchangedata(UiToggleArgs *args, void *onchangedata){ + args->onchangedata = onchangedata; } -void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback){ - args->onclick = callback; +void ui_toggle_args_set_varname(UiToggleArgs *args, const char *varname) { + args->varname = strdup(varname); } +void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value) { + args->value = value; +} -void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata){ - args->onclickdata = onclickdata; +void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group) { + args->enable_group = group; } -void ui_button_args_set_groups(UiButtonArgs *args, int *groups){ - // TODO +void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; } -void ui_button_args_free(UiButtonArgs *args) { +void ui_toggle_args_free(UiToggleArgs *args) { free((void*)args->name); free((void*)args->style_class); free((void*)args->label); - free((void*)args->stockid); free((void*)args->icon); + free((void*)args->tooltip); + free((void*)args->varname); free((void*)args->groups); free(args); } - -/* ------------------------- UiToggleArgs ----------------------------*/ +/* ------------------------- UiLinkButtonArgs ----------------------------*/ -UiToggleArgs* ui_toggle_args_new(void) { - UiToggleArgs *args = malloc(sizeof(UiToggleArgs)); - memset(args, 0, sizeof(UiToggleArgs)); +UiLinkButtonArgs* ui_linkbutton_args_new(void) { + UiLinkButtonArgs *args = malloc(sizeof(UiLinkButtonArgs)); + memset(args, 0, sizeof(UiLinkButtonArgs)); return args; } -void ui_toggle_args_set_fill(UiToggleArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; +void ui_linkbutton_args_set_fill(UiLinkButtonArgs *args, UiBool fill) { + args->fill = fill; } -void ui_toggle_args_set_hexpand(UiToggleArgs *args, UiBool value) { +void ui_linkbutton_args_set_hexpand(UiLinkButtonArgs *args, UiBool value) { args->hexpand = value; } -void ui_toggle_args_set_vexpand(UiToggleArgs *args, UiBool value) { +void ui_linkbutton_args_set_vexpand(UiLinkButtonArgs *args, UiBool value) { args->vexpand = value; } -void ui_toggle_args_set_hfill(UiToggleArgs *args, UiBool value) { +void ui_linkbutton_args_set_hfill(UiLinkButtonArgs *args, UiBool value) { args->hfill = value; } -void ui_toggle_args_set_vfill(UiToggleArgs *args, UiBool value) { +void ui_linkbutton_args_set_vfill(UiLinkButtonArgs *args, UiBool value) { args->vfill = value; } -void ui_toggle_args_set_override_defaults(UiToggleArgs *args, UiBool value) { +void ui_linkbutton_args_set_override_defaults(UiLinkButtonArgs *args, UiBool value) { args->override_defaults = value; } +void ui_linkbutton_args_set_margin(UiLinkButtonArgs *args, int value) { + args->margin = value; +} -void ui_toggle_args_set_colspan(UiToggleArgs *args, int colspan) { +void ui_linkbutton_args_set_margin_left(UiLinkButtonArgs *args, int value) { + args->margin_left = value; +} + +void ui_linkbutton_args_set_margin_right(UiLinkButtonArgs *args, int value) { + args->margin_right = value; +} + +void ui_linkbutton_args_set_margin_top(UiLinkButtonArgs *args, int value) { + args->margin_top = value; +} + +void ui_linkbutton_args_set_margin_bottom(UiLinkButtonArgs *args, int value) { + args->margin_bottom = value; +} + +void ui_linkbutton_args_set_colspan(UiLinkButtonArgs *args, int colspan) { args->colspan = colspan; } -void ui_toggle_args_set_rowspan(UiToggleArgs *args, int rowspan) { +void ui_linkbutton_args_set_rowspan(UiLinkButtonArgs *args, int rowspan) { args->rowspan = rowspan; } -void ui_toggle_args_set_name(UiToggleArgs *args, const char *name) { +void ui_linkbutton_args_set_name(UiLinkButtonArgs *args, const char *name) { args->name = strdup(name); } -void ui_toggle_args_set_style_class(UiToggleArgs *args, const char *classname) { +void ui_linkbutton_args_set_style_class(UiLinkButtonArgs *args, const char *classname) { args->style_class = strdup(classname); } -void ui_toggle_args_set_label(UiToggleArgs *args, const char *label){ +void ui_linkbutton_args_set_label(UiLinkButtonArgs *args, const char *label){ args->label = strdup(label); } - -void ui_toggle_args_set_stockid(UiToggleArgs *args, const char *stockid){ - args->stockid = strdup(stockid); +void ui_linkbutton_args_set_uri(UiLinkButtonArgs *args, const char *uri) { + args->uri = strdup(uri); } - -void ui_toggle_args_set_icon(UiToggleArgs *args, const char *icon){ - args->icon = strdup(icon); +void ui_linkbutton_args_set_onclick(UiLinkButtonArgs *args, ui_callback callback) { + args->onclick = callback; } - -void ui_toggle_args_set_labeltype(UiToggleArgs *args, int labeltype){ - args->labeltype = labeltype; +void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata) { + args->onclickdata = userdata; } -void ui_toggle_args_set_onchange(UiToggleArgs *args, ui_callback callback){ - args->onchange = callback; +void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value) { + args->nofollow = value; } - -void ui_toggle_args_set_onchangedata(UiToggleArgs *args, void *onchangedata){ - args->onchangedata = onchangedata; +void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type) { + args->type = type; } -void ui_toggle_args_set_varname(UiToggleArgs *args, const char *varname) { +void ui_linkbutton_args_set_varname(UiLinkButtonArgs *args, const char *varname) { args->varname = strdup(varname); } -void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value) { +void ui_linkbutton_args_set_value(UiLinkButtonArgs *args, UiString *value) { args->value = value; } -void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group) { - args->enable_group = group; -} - -void ui_toggle_args_set_groups(UiToggleArgs *args, int *groups){ - // TODO +void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; } -void ui_toggle_args_free(UiToggleArgs *args) { +void ui_linkbutton_args_free(UiLinkButtonArgs *args) { free((void*)args->name); free((void*)args->style_class); free((void*)args->label); - free((void*)args->stockid); - free((void*)args->icon); + free((void*)args->uri); free((void*)args->varname); free((void*)args->groups); free(args); @@ -1069,7 +1659,7 @@ UiListArgs* ui_list_args_new(void) { } void ui_list_args_set_fill(UiListArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } void ui_list_args_set_hexpand(UiListArgs *args, UiBool value) { @@ -1092,6 +1682,26 @@ void ui_list_args_set_override_defaults(UiListArgs *args, UiBool value) { args->override_defaults = value; } +void ui_list_args_set_margin(UiListArgs *args, int value) { + args->margin = value; +} + +void ui_list_args_set_margin_left(UiListArgs *args, int value) { + args->margin_left = value; +} + +void ui_list_args_set_margin_right(UiListArgs *args, int value) { + args->margin_right = value; +} + +void ui_list_args_set_margin_top(UiListArgs *args, int value) { + args->margin_top = value; +} + +void ui_list_args_set_margin_bottom(UiListArgs *args, int value) { + args->margin_bottom = value; +} + void ui_list_args_set_colspan(UiListArgs *args, int colspan) { args->colspan = colspan; } @@ -1141,6 +1751,14 @@ void ui_list_args_set_getvalue_data(UiListArgs *args, void *userdata) { args->getvalue2data = userdata; } +void ui_list_args_set_getstyle_func(UiListArgs *args, ui_getstylefunc getstyle) { + args->getstyle = getstyle; +} + +void ui_list_args_set_getstyle_data(UiListArgs *args, void *userdata) { + args->getstyledata = userdata; +} + void ui_list_args_set_onactivate(UiListArgs *args, ui_callback callback) { args->onactivate = callback; } @@ -1181,6 +1799,14 @@ void ui_list_args_set_ondropdata(UiListArgs *args, void *userdata) { args->ondropdata = userdata; } +void ui_list_args_set_onsave(UiListArgs *args, ui_list_savefunc onsave) { + args->onsave = onsave; +} + +void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata) { + args->onsavedata = userdata; +} + void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection) { args->multiselection = multiselection; } @@ -1189,8 +1815,10 @@ void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder) args->contextmenu = menubuilder; } -void ui_list_args_set_groups(UiListArgs *args, int *groups) { - // TODO +void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; } void ui_list_args_free(UiListArgs *args) { @@ -1203,6 +1831,7 @@ void ui_list_args_free(UiListArgs *args) { } free(args->static_elements); } + free((void*)args->groups); free(args); } @@ -1218,7 +1847,7 @@ UiSourceListArgs* ui_sourcelist_args_new(void) { void ui_sourcelist_args_set_fill(UiSourceListArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } @@ -1246,6 +1875,25 @@ void ui_sourcelist_args_set_override_defaults(UiSourceListArgs *args, UiBool val args->override_defaults = value; } +void ui_sourcelist_args_set_margin(UiSourceListArgs *args, int value) { + args->margin = value; +} + +void ui_sourcelist_args_set_margin_left(UiSourceListArgs *args, int value) { + args->margin_left = value; +} + +void ui_sourcelist_args_set_margin_right(UiSourceListArgs *args, int value) { + args->margin_right = value; +} + +void ui_sourcelist_args_set_margin_top(UiSourceListArgs *args, int value) { + args->margin_top = value; +} + +void ui_sourcelist_args_set_margin_bottom(UiSourceListArgs *args, int value) { + args->margin_bottom = value; +} void ui_sourcelist_args_set_colspan(UiSourceListArgs *args, int colspan) { args->colspan = colspan; @@ -1310,12 +1958,20 @@ void ui_sourcelist_args_set_onbuttonclickdata(UiSourceListArgs *args, void *user args->onbuttonclickdata = userdata; } +void ui_sourcelist_args_set_contextmenu(UiSourceListArgs *args, UiMenuBuilder *menubuilder) { + args->contextmenu = menubuilder; +} + +void ui_sourcelist_args_set_header_is_item(UiSourceListArgs *args, UiBool value) { + args->header_is_item = value; +} void ui_sourcelist_args_free(UiSourceListArgs *args) { free((void*)args->name); free((void*)args->style_class); free((void*)args->varname); free((void*)args->sublists); + free((void*)args->groups); free(args); } @@ -1330,7 +1986,7 @@ UiTextAreaArgs* ui_textarea_args_new(void) { void ui_textarea_args_set_fill(UiTextAreaArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } @@ -1358,6 +2014,26 @@ void ui_textarea_args_set_override_defaults(UiTextAreaArgs *args, UiBool value) args->override_defaults = value; } +void ui_textarea_args_set_margin(UiTextAreaArgs *args, int value) { + args->margin = value; +} + +void ui_textarea_args_set_margin_left(UiTextAreaArgs *args, int value) { + args->margin_left = value; +} + +void ui_textarea_args_set_margin_right(UiTextAreaArgs *args, int value) { + args->margin_right = value; +} + +void ui_textarea_args_set_margin_top(UiTextAreaArgs *args, int value) { + args->margin_top = value; +} + +void ui_textarea_args_set_margin_bottom(UiTextAreaArgs *args, int value) { + args->margin_bottom = value; +} + void ui_textarea_args_set_colspan(UiTextAreaArgs *args, int colspan) { args->colspan = colspan; @@ -1395,8 +2071,10 @@ void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value) { args->value = value; } -void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *groups){ - // TODO +void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; } void ui_textarea_args_free(UiTextAreaArgs *args) { @@ -1419,7 +2097,7 @@ UiTextFieldArgs* ui_textfield_args_new(void) { void ui_textfield_args_set_fill(UiTextFieldArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } @@ -1447,6 +2125,26 @@ void ui_textfield_args_set_override_defaults(UiTextFieldArgs *args, UiBool value args->override_defaults = value; } +void ui_textfield_args_set_margin(UiTextFieldArgs *args, int value) { + args->margin = value; +} + +void ui_textfield_args_set_margin_left(UiTextFieldArgs *args, int value) { + args->margin_left = value; +} + +void ui_textfield_args_set_margin_right(UiTextFieldArgs *args, int value) { + args->margin_right = value; +} + +void ui_textfield_args_set_margin_top(UiTextFieldArgs *args, int value) { + args->margin_top = value; +} + +void ui_textfield_args_set_margin_bottom(UiTextFieldArgs *args, int value) { + args->margin_bottom = value; +} + void ui_textfield_args_set_colspan(UiTextFieldArgs *args, int colspan) { args->colspan = colspan; @@ -1493,8 +2191,10 @@ void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value) { args->value = value; } -void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *groups){ - // TODO +void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; } void ui_textfield_args_free(UiTextFieldArgs *args) { @@ -1506,6 +2206,129 @@ void ui_textfield_args_free(UiTextFieldArgs *args) { } +/* ------------------------- UiSpinBoxArgs ----------------------------*/ + +UiSpinBoxArgs* ui_spinbox_args_new(void) { + UiSpinBoxArgs *args = malloc(sizeof(UiSpinBoxArgs)); + memset(args, 0, sizeof(UiSpinBoxArgs)); + return args; +} + +void ui_spinbox_args_set_fill(UiSpinBoxArgs *args, UiBool fill) { + args->fill = fill; +} + +void ui_spinbox_args_set_hexpand(UiSpinBoxArgs *args, UiBool value) { + args->hexpand = value; +} + +void ui_spinbox_args_set_vexpand(UiSpinBoxArgs *args, UiBool value) { + args->vexpand = value; +} + +void ui_spinbox_args_set_hfill(UiSpinBoxArgs *args, UiBool value) { + args->hfill = value; +} + +void ui_spinbox_args_set_vfill(UiSpinBoxArgs *args, UiBool value) { + args->vfill = value; +} + +void ui_spinbox_args_set_override_defaults(UiSpinBoxArgs *args, UiBool value) { + args->override_defaults = value; +} + +void ui_spinbox_args_set_colspan(UiSpinBoxArgs *args, int colspan) { + args->colspan = colspan; +} + +void ui_spinbox_args_set_rowspan(UiSpinBoxArgs *args, int rowspan) { + args->rowspan = rowspan; +} + +void ui_spinbox_args_set_name(UiSpinBoxArgs *args, const char *name) { + args->name = strdup(name); +} + +void ui_spinbox_args_set_style_class(UiSpinBoxArgs *args, const char *classname) { + args->style_class = strdup(classname); +} + +void ui_spinbox_args_set_onchange(UiSpinBoxArgs *args, ui_callback callback) { + args->onchange = callback; +} + +void ui_spinbox_args_set_onchangedata(UiSpinBoxArgs *args, void *onchangedata) { + args->onchangedata = onchangedata; +} + +void ui_spinbox_args_set_margin(UiSpinBoxArgs *args, int value) { + args->margin = value; +} + +void ui_spinbox_args_set_margin_left(UiSpinBoxArgs *args, int value) { + args->margin_left = value; +} + +void ui_spinbox_args_set_margin_right(UiSpinBoxArgs *args, int value) { + args->margin_right = value; +} + +void ui_spinbox_args_set_margin_top(UiSpinBoxArgs *args, int value) { + args->margin_top = value; +} + +void ui_spinbox_args_set_margin_bottom(UiSpinBoxArgs *args, int value) { + args->margin_bottom = value; +} + +void ui_spinbox_args_set_min(UiSpinBoxArgs *args, double min) { + args->min = min; +} + +void ui_spinbox_args_set_max(UiSpinBoxArgs *args, double max) { + args->max = max; +} + +void ui_spinbox_args_set_step(UiSpinBoxArgs *args, double step) { + args->step = step; +} + +void ui_spinbox_args_set_digits(UiSpinBoxArgs *args, int digits) { + args->digits = digits; +} + +void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varname) { + args->varname = strdup(varname); +} + +void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value) { + args->intvalue = value; +} + +void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value) { + args->doublevalue = value; +} + +void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value) { + args->rangevalue = value; +} + +void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; +} + +void ui_spinbox_args_free(UiSpinBoxArgs *args) { + free((void*)args->name); + free((void*)args->style_class); + free((void*)args->varname); + free((void*)args->groups); + free(args); +} + + /* ------------------------- UiWebviewArgs ----------------------------*/ UiWebviewArgs* ui_webview_args_new(void) { @@ -1516,7 +2339,7 @@ UiWebviewArgs* ui_webview_args_new(void) { void ui_webview_args_set_fill(UiWebviewArgs *args, UiBool fill) { - args->fill = fill ? UI_ON : UI_OFF; + args->fill = fill; } @@ -1544,6 +2367,25 @@ void ui_webview_args_set_override_defaults(UiWebviewArgs *args, UiBool value) { args->override_defaults = value; } +void ui_webview_args_set_margin(UiWebviewArgs *args, int value) { + args->margin = value; +} + +void ui_webview_args_set_margin_left(UiWebviewArgs *args, int value) { + args->margin_left = value; +} + +void ui_webview_args_set_margin_right(UiWebviewArgs *args, int value) { + args->margin_right = value; +} + +void ui_webview_args_set_margin_top(UiWebviewArgs *args, int value) { + args->margin_top = value; +} + +void ui_webview_args_set_margin_bottom(UiWebviewArgs *args, int value) { + args->margin_bottom = value; +} void ui_webview_args_set_colspan(UiWebviewArgs *args, int colspan) { args->colspan = colspan; @@ -1572,8 +2414,10 @@ void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value) { args->value = value; } -void ui_webview_args_set_groups(UiWebviewArgs *args, int *groups){ - // TODO +void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates) { + args->groups = calloc(numstates+1, sizeof(int)); + memcpy((void*)args->groups, states, numstates * sizeof(int)); + ((int*)args->groups)[numstates] = -1; } void ui_webview_args_free(UiWebviewArgs *args) { diff --git a/ui/common/args.h b/ui/common/args.h index 04e7063..e533193 100644 --- a/ui/common/args.h +++ b/ui/common/args.h @@ -29,23 +29,57 @@ #ifndef UIC_ARGS_H #define UIC_ARGS_H +#include "../ui/window.h" #include "../ui/container.h" #include "../ui/display.h" #include "../ui/button.h" +#include "../ui/entry.h" #include "../ui/menu.h" #include "../ui/toolbar.h" #include "../ui/tree.h" #include "../ui/text.h" #include "../ui/webview.h" +#include "../ui/widget.h" #ifdef __cplusplus extern "C" { #endif +UIEXPORT UiDialogArgs* ui_dialog_args_new(void); +UIEXPORT void ui_dialog_args_set_title(UiDialogArgs *args, const char *title); +UIEXPORT void ui_dialog_args_set_content(UiDialogArgs *args, const char *str); +UIEXPORT void ui_dialog_args_set_button1_label(UiDialogArgs *args, const char *label); +UIEXPORT void ui_dialog_args_set_button2_label(UiDialogArgs *args, const char *label); +UIEXPORT void ui_dialog_args_set_closebutton_label(UiDialogArgs *args, const char *label); +UIEXPORT void ui_dialog_args_set_input_value(UiDialogArgs *args, const char *value); +UIEXPORT void ui_dialog_args_set_input(UiDialogArgs *args, UiBool input); +UIEXPORT void ui_dialog_args_set_password(UiDialogArgs *args, UiBool password); +UIEXPORT void ui_dialog_args_set_result(UiDialogArgs *args, ui_callback cb); +UIEXPORT void ui_dialog_args_set_resultdata(UiDialogArgs *args, void *userdata); +UIEXPORT void ui_dialog_args_free(UiDialogArgs *args); + +UIEXPORT UiDialogWindowArgs* ui_dialogwindow_args_new(void); +UIEXPORT void ui_dialogwindow_args_set_modal(UiDialogWindowArgs *args, UiTri value); +UIEXPORT void ui_dialogwindow_args_set_titlebar_buttons(UiDialogWindowArgs *args, UiTri value); +UIEXPORT void ui_dialogwindow_args_set_show_closebutton(UiDialogWindowArgs *args, UiTri value); +UIEXPORT void ui_dialogwindow_args_set_title(UiDialogWindowArgs *args, const char *title); +UIEXPORT void ui_dialogwindow_args_set_lbutton1(UiDialogWindowArgs *args, const char *label); +UIEXPORT void ui_dialogwindow_args_set_lbutton2(UiDialogWindowArgs *args, const char *label); +UIEXPORT void ui_dialogwindow_args_set_rbutton3(UiDialogWindowArgs *args, const char *label); +UIEXPORT void ui_dialogwindow_args_set_rbutton4(UiDialogWindowArgs *args, const char *label); +UIEXPORT void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states); +UIEXPORT void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states); +UIEXPORT void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states); +UIEXPORT void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states); +UIEXPORT void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button); +UIEXPORT void ui_dialogwindow_args_set_width(UiDialogWindowArgs *args, int width); +UIEXPORT void ui_dialogwindow_args_set_height(UiDialogWindowArgs *args, int height); +UIEXPORT void ui_dialogwindow_args_set_onclick(UiDialogWindowArgs *args, ui_callback cb); +UIEXPORT void ui_dialogwindow_args_set_onclickdata(UiDialogWindowArgs *args, void *userdata); +UIEXPORT void ui_dialogwindow_args_free(UiDialogWindowArgs *args); UIEXPORT UiMenuItemArgs* ui_menuitem_args_new(void); UIEXPORT void ui_menuitem_args_set_label(UiMenuItemArgs *args, const char *label); -UIEXPORT void ui_menuitem_args_set_stockid(UiMenuItemArgs *args, const char *stockid); UIEXPORT void ui_menuitem_args_set_icon(UiMenuItemArgs *args, const char *icon); UIEXPORT void ui_menuitem_args_set_onclick(UiMenuItemArgs *args, ui_callback callback); UIEXPORT void ui_menuitem_args_set_onclickdata(UiMenuItemArgs *args, void *onclickdata); @@ -53,7 +87,6 @@ UIEXPORT void ui_menuitem_args_free(UiMenuItemArgs *args); UIEXPORT UiMenuToggleItemArgs* ui_menutoggleitem_args_new(void); UIEXPORT void ui_menutoggleitem_args_set_label(UiMenuToggleItemArgs *args, const char *label); -UIEXPORT void ui_menutoggleitem_args_set_stockid(UiMenuToggleItemArgs *args, const char *stockid); UIEXPORT void ui_menutoggleitem_args_set_icon(UiMenuToggleItemArgs *args, const char *icon); UIEXPORT void ui_menutoggleitem_args_set_varname(UiMenuToggleItemArgs *args, const char *varname); UIEXPORT void ui_menutoggleitem_args_set_onchange(UiMenuToggleItemArgs *args, ui_callback callback); @@ -70,27 +103,27 @@ UIEXPORT void ui_menuitemlist_args_free(UiMenuItemListArgs *args); UIEXPORT UiToolbarItemArgs* ui_toolbar_item_args_new(void); UIEXPORT void ui_toolbar_item_args_set_label(UiToolbarItemArgs *args, const char *label); -UIEXPORT void ui_toolbar_item_args_set_stockid(UiToolbarItemArgs *args, const char *stockid); UIEXPORT void ui_toolbar_item_args_set_icon(UiToolbarItemArgs *args, const char *icon); +UIEXPORT void ui_toolbar_item_args_set_tooltip(UiToolbarItemArgs *args, const char *tooltip); UIEXPORT void ui_toolbar_item_args_set_onclick(UiToolbarItemArgs *args, ui_callback callback); UIEXPORT void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclickdata); -UIEXPORT void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *groups); +UIEXPORT void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates); UIEXPORT void ui_toolbar_item_args_free(UiToolbarItemArgs *args); UIEXPORT UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void); UIEXPORT void ui_toolbar_toggleitem_args_set_label(UiToolbarToggleItemArgs *args, const char *label); -UIEXPORT void ui_toolbar_toggleitem_args_set_stockid(UiToolbarToggleItemArgs *args, const char *stockid); UIEXPORT void ui_toolbar_toggleitem_args_set_icon(UiToolbarToggleItemArgs *args, const char *icon); +UIEXPORT void ui_toolbar_toggleitem_args_set_tooltip(UiToolbarToggleItemArgs *args, const char *tooltip); UIEXPORT void ui_toolbar_toggleitem_args_set_varname(UiToolbarToggleItemArgs *args, const char *varname); UIEXPORT void ui_toolbar_toggleitem_args_set_onchange(UiToolbarToggleItemArgs *args, ui_callback callback); UIEXPORT void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, void *onchangedata); -UIEXPORT void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *groups); +UIEXPORT void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *states, int numstates); UIEXPORT void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args); UIEXPORT UiToolbarMenuArgs* ui_toolbar_menu_args_new(void); UIEXPORT void ui_toolbar_menu_args_set_label(UiToolbarMenuArgs *args, const char *label); -UIEXPORT void ui_toolbar_menu_args_set_stockid(UiToolbarMenuArgs *args, const char *stockid); UIEXPORT void ui_toolbar_menu_args_set_icon(UiToolbarMenuArgs *args, const char *icon); +UIEXPORT void ui_toolbar_menu_args_set_tooltip(UiToolbarMenuArgs *args, const char *tooltip); UIEXPORT void ui_toolbar_menu_args_free(UiToolbarMenuArgs *args); UIEXPORT UiContainerArgs* ui_container_args_new(void); @@ -100,6 +133,11 @@ UIEXPORT void ui_container_args_set_vexpand(UiContainerArgs *args, UiBool value) UIEXPORT void ui_container_args_set_hfill(UiContainerArgs *args, UiBool value); UIEXPORT void ui_container_args_set_vfill(UiContainerArgs *args, UiBool value); UIEXPORT void ui_container_args_set_override_defaults(UiContainerArgs *args, UiBool value); +UIEXPORT void ui_container_args_set_margin(UiContainerArgs *args, int value); +UIEXPORT void ui_container_args_set_margin_left(UiContainerArgs *args, int value); +UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value); +UIEXPORT void ui_container_args_set_margin_top(UiContainerArgs *args, int value); +UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value); UIEXPORT void ui_container_args_set_colspan(UiContainerArgs *args, int colspan); UIEXPORT void ui_container_args_set_rowspan(UiContainerArgs *args, int rowspan); UIEXPORT void ui_container_args_set_def_hexpand(UiContainerArgs *args, UiBool value); @@ -108,13 +146,11 @@ UIEXPORT void ui_container_args_set_def_hfill(UiContainerArgs *args, UiBool valu UIEXPORT void ui_container_args_set_def_vfill(UiContainerArgs *args, UiBool value); UIEXPORT void ui_container_args_set_name(UiContainerArgs *args, const char *name); UIEXPORT void ui_container_args_set_style_class(UiContainerArgs *args, const char *classname); -UIEXPORT void ui_container_args_set_margin(UiContainerArgs *args, int value); UIEXPORT void ui_container_args_set_spacing(UiContainerArgs *args, int value); UIEXPORT void ui_container_args_set_columnspacing(UiContainerArgs *args, int value); UIEXPORT void ui_container_args_set_rowspacing(UiContainerArgs *args, int value); UIEXPORT void ui_container_args_free(UiContainerArgs *args); - UIEXPORT UiFrameArgs* ui_frame_args_new(void); UIEXPORT void ui_frame_args_set_fill(UiFrameArgs *args, UiBool fill); UIEXPORT void ui_frame_args_set_hexpand(UiFrameArgs *args, UiBool value); @@ -122,11 +158,17 @@ UIEXPORT void ui_frame_args_set_vexpand(UiFrameArgs *args, UiBool value); UIEXPORT void ui_frame_args_set_hfill(UiFrameArgs *args, UiBool value); UIEXPORT void ui_frame_args_set_vfill(UiFrameArgs *args, UiBool value); UIEXPORT void ui_frame_args_set_override_defaults(UiFrameArgs *args, UiBool value); +UIEXPORT void ui_frame_args_set_margin(UiFrameArgs *args, int value); +UIEXPORT void ui_frame_args_set_margin_left(UiFrameArgs *args, int value); +UIEXPORT void ui_frame_args_set_margin_right(UiFrameArgs *args, int value); +UIEXPORT void ui_frame_args_set_margin_top(UiFrameArgs *args, int value); +UIEXPORT void ui_frame_args_set_margin_bottom(UiFrameArgs *args, int value); UIEXPORT void ui_frame_args_set_colspan(UiFrameArgs *args, int colspan); UIEXPORT void ui_frame_args_set_rowspan(UiFrameArgs *args, int rowspan); UIEXPORT void ui_frame_args_set_name(UiFrameArgs *args, const char *name); UIEXPORT void ui_frame_args_set_style_class(UiFrameArgs *args, const char *classname); -UIEXPORT void ui_frame_args_set_margin(UiFrameArgs *args, int value); +UIEXPORT void ui_frame_args_set_subcontainer(UiFrameArgs *args, UiSubContainerType subcontainer); +UIEXPORT void ui_frame_args_set_padding(UiFrameArgs *args, int value); UIEXPORT void ui_frame_args_set_spacing(UiFrameArgs *args, int value); UIEXPORT void ui_frame_args_set_columnspacing(UiFrameArgs *args, int value); UIEXPORT void ui_frame_args_set_rowspacing(UiFrameArgs *args, int value); @@ -138,6 +180,10 @@ UIEXPORT UiSidebarArgs* ui_sidebar_args_new(void); UIEXPORT void ui_sidebar_args_set_name(UiSidebarArgs *args, const char *name); UIEXPORT void ui_sidebar_args_set_style_class(UiSidebarArgs *args, const char *classname); UIEXPORT void ui_sidebar_args_set_margin(UiSidebarArgs *args, int value); +UIEXPORT void ui_sidebar_args_set_margin_left(UiSidebarArgs *args, int value); +UIEXPORT void ui_sidebar_args_set_margin_right(UiSidebarArgs *args, int value); +UIEXPORT void ui_sidebar_args_set_margin_top(UiSidebarArgs *args, int value); +UIEXPORT void ui_sidebar_args_set_margin_bottom(UiSidebarArgs *args, int value); UIEXPORT void ui_sidebar_args_set_spacing(UiSidebarArgs *args, int value); UIEXPORT void ui_sidebar_args_free(UiSidebarArgs *args); @@ -148,20 +194,71 @@ UIEXPORT void ui_splitpane_args_set_vexpand(UiSplitPaneArgs *args, UiBool value) UIEXPORT void ui_splitpane_args_set_hfill(UiSplitPaneArgs *args, UiBool value); UIEXPORT void ui_splitpane_args_set_vfill(UiSplitPaneArgs *args, UiBool value); UIEXPORT void ui_splitpane_args_set_override_defaults(UiSplitPaneArgs *args, UiBool value); +UIEXPORT void ui_splitpane_args_set_margin(UiSplitPaneArgs *args, int value); +UIEXPORT void ui_splitpane_args_set_margin_left(UiSplitPaneArgs *args, int value); +UIEXPORT void ui_splitpane_args_set_margin_right(UiSplitPaneArgs *args, int value); +UIEXPORT void ui_splitpane_args_set_margin_top(UiSplitPaneArgs *args, int value); +UIEXPORT void ui_splitpane_args_set_margin_bottom(UiSplitPaneArgs *args, int value); UIEXPORT void ui_splitpane_args_set_colspan(UiSplitPaneArgs *args, int colspan); UIEXPORT void ui_splitpane_args_set_rowspan(UiSplitPaneArgs *args, int rowspan); UIEXPORT void ui_splitpane_args_set_name(UiSplitPaneArgs *args, const char *name); UIEXPORT void ui_splitpane_args_set_style_class(UiSplitPaneArgs *args, const char *classname); -UIEXPORT void ui_splitpane_args_set_margin(UiSplitPaneArgs *args, int value); UIEXPORT void ui_splitpane_args_set_spacing(UiSplitPaneArgs *args, int value); UIEXPORT void ui_splitpane_args_set_columnspacing(UiSplitPaneArgs *args, int value); UIEXPORT void ui_splitpane_args_set_rowspacing(UiSplitPaneArgs *args, int value); UIEXPORT void ui_splitpane_args_set_initial_position(UiSplitPaneArgs *args, int pos); +UIEXPORT void ui_splitpane_args_set_position_property(UiSplitPaneArgs *args, const char *propname); UIEXPORT void ui_splitpane_args_set_varname(UiSplitPaneArgs *args, const char *varname); UIEXPORT void ui_splitpane_args_set_value(UiSplitPaneArgs *args, UiInteger *value); UIEXPORT void ui_splitpane_args_set_max_panes(UiSplitPaneArgs *args, int max); UIEXPORT void ui_splitpane_args_free(UiSplitPaneArgs *args); +UIEXPORT UiTabViewArgs* ui_tabview_args_new(void); +UIEXPORT void ui_tabview_args_set_fill(UiTabViewArgs *args, UiBool fill); +UIEXPORT void ui_tabview_args_set_hexpand(UiTabViewArgs *args, UiBool value); +UIEXPORT void ui_tabview_args_set_vexpand(UiTabViewArgs *args, UiBool value); +UIEXPORT void ui_tabview_args_set_hfill(UiTabViewArgs *args, UiBool value); +UIEXPORT void ui_tabview_args_set_vfill(UiTabViewArgs *args, UiBool value); +UIEXPORT void ui_tabview_args_set_override_defaults(UiTabViewArgs *args, UiBool value); +UIEXPORT void ui_tabview_args_set_margin(UiTabViewArgs *args, int value); +UIEXPORT void ui_tabview_args_set_margin_left(UiTabViewArgs *args, int value); +UIEXPORT void ui_tabview_args_set_margin_right(UiTabViewArgs *args, int value); +UIEXPORT void ui_tabview_args_set_margin_top(UiTabViewArgs *args, int value); +UIEXPORT void ui_tabview_args_set_margin_bottom(UiTabViewArgs *args, int value); +UIEXPORT void ui_tabview_args_set_colspan(UiTabViewArgs *args, int colspan); +UIEXPORT void ui_tabview_args_set_rowspan(UiTabViewArgs *args, int rowspan); +UIEXPORT void ui_tabview_args_set_name(UiTabViewArgs *args, const char *name); +UIEXPORT void ui_tabview_args_set_style_class(UiTabViewArgs *args, const char *classname); +UIEXPORT void ui_tabview_args_set_padding(UiTabViewArgs *args, int value); +UIEXPORT void ui_tabview_args_set_spacing(UiTabViewArgs *args, int value); +UIEXPORT void ui_tabview_args_set_columnspacing(UiTabViewArgs *args, int value); +UIEXPORT void ui_tabview_args_set_rowspacing(UiTabViewArgs *args, int value); +UIEXPORT void ui_tabview_args_set_type(UiTabViewArgs *args, UiTabViewType tabview); +UIEXPORT void ui_tabview_args_set_onchange(UiTabViewArgs *args, ui_callback cb); +UIEXPORT void ui_tabview_args_set_onchangedata(UiTabViewArgs *args, void *userdata); +UIEXPORT void ui_tabview_args_set_varname(UiTabViewArgs *args, const char *varname); +UIEXPORT void ui_tabview_args_set_value(UiTabViewArgs *args, UiInteger *value); +UIEXPORT void ui_tabview_args_set_subcontainer(UiTabViewArgs *args, UiSubContainerType subcontainer); +UIEXPORT void ui_tabview_args_free(UiTabViewArgs *args); + +UIEXPORT UiWidgetArgs* ui_widget_args_new(void); +UIEXPORT void ui_widget_args_set_fill(UiWidgetArgs *args, UiBool fill); +UIEXPORT void ui_widget_args_set_hexpand(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_vexpand(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_hfill(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_vfill(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_override_defaults(UiWidgetArgs *args, UiBool value); +UIEXPORT void ui_widget_args_set_margin(UiWidgetArgs *args, int value); +UIEXPORT void ui_widget_args_set_margin_left(UiWidgetArgs *args, int value); +UIEXPORT void ui_widget_args_set_margin_right(UiWidgetArgs *args, int value); +UIEXPORT void ui_widget_args_set_margin_top(UiWidgetArgs *args, int value); +UIEXPORT void ui_widget_args_set_margin_bottom(UiWidgetArgs *args, int value); +UIEXPORT void ui_widget_args_set_colspan(UiWidgetArgs *args, int colspan); +UIEXPORT void ui_widget_args_set_rowspan(UiWidgetArgs *args, int rowspan); +UIEXPORT void ui_widget_args_set_name(UiWidgetArgs *args, const char *name); +UIEXPORT void ui_widget_args_set_style_class(UiWidgetArgs *args, const char *classname); +UIEXPORT void ui_widget_args_free(UiWidgetArgs *args); + UIEXPORT UiLabelArgs* ui_label_args_new(void); UIEXPORT void ui_label_args_set_fill(UiLabelArgs *args, UiBool fill); UIEXPORT void ui_label_args_set_hexpand(UiLabelArgs *args, UiBool value); @@ -169,6 +266,11 @@ UIEXPORT void ui_label_args_set_vexpand(UiLabelArgs *args, UiBool value); UIEXPORT void ui_label_args_set_hfill(UiLabelArgs *args, UiBool value); UIEXPORT void ui_label_args_set_vfill(UiLabelArgs *args, UiBool value); UIEXPORT void ui_label_args_set_override_defaults(UiLabelArgs *args, UiBool value); +UIEXPORT void ui_label_args_set_margin(UiLabelArgs *args, int value); +UIEXPORT void ui_label_args_set_margin_left(UiLabelArgs *args, int value); +UIEXPORT void ui_label_args_set_margin_right(UiLabelArgs *args, int value); +UIEXPORT void ui_label_args_set_margin_top(UiLabelArgs *args, int value); +UIEXPORT void ui_label_args_set_margin_bottom(UiLabelArgs *args, int value); UIEXPORT void ui_label_args_set_colspan(UiLabelArgs *args, int colspan); UIEXPORT void ui_label_args_set_rowspan(UiLabelArgs *args, int rowspan); UIEXPORT void ui_label_args_set_name(UiLabelArgs *args, const char *name); @@ -187,6 +289,11 @@ UIEXPORT void ui_progressbar_args_set_vexpand(UiProgressbarArgs *args, UiBool va UIEXPORT void ui_progressbar_args_set_hfill(UiProgressbarArgs *args, UiBool value); UIEXPORT void ui_progressbar_args_set_vfill(UiProgressbarArgs *args, UiBool value); UIEXPORT void ui_progressbar_args_set_override_defaults(UiProgressbarArgs *args, UiBool value); +UIEXPORT void ui_progressbar_args_set_margin(UiProgressbarArgs *args, int value); +UIEXPORT void ui_progressbar_args_set_margin_left(UiProgressbarArgs *args, int value); +UIEXPORT void ui_progressbar_args_set_margin_right(UiProgressbarArgs *args, int value); +UIEXPORT void ui_progressbar_args_set_margin_top(UiProgressbarArgs *args, int value); +UIEXPORT void ui_progressbar_args_set_margin_bottom(UiProgressbarArgs *args, int value); UIEXPORT void ui_progressbar_args_set_colspan(UiProgressbarArgs *args, int colspan); UIEXPORT void ui_progressbar_args_set_rowspan(UiProgressbarArgs *args, int rowspan); UIEXPORT void ui_progressbar_args_set_name(UiProgressbarArgs *args, const char *name); @@ -204,6 +311,11 @@ UIEXPORT void ui_progress_spinner_args_set_vexpand(UiProgressbarSpinnerArgs *arg UIEXPORT void ui_progress_spinner_args_set_hfill(UiProgressbarSpinnerArgs *args, UiBool value); UIEXPORT void ui_progress_spinner_args_set_vfill(UiProgressbarSpinnerArgs *args, UiBool value); UIEXPORT void ui_progress_spinner_args_set_override_defaults(UiProgressbarSpinnerArgs *args, UiBool value); +UIEXPORT void ui_progress_spinner_args_set_margin(UiProgressbarSpinnerArgs *args, int value); +UIEXPORT void ui_progress_spinner_args_set_margin_left(UiProgressbarSpinnerArgs *args, int value); +UIEXPORT void ui_progress_spinner_args_set_margin_right(UiProgressbarSpinnerArgs *args, int value); +UIEXPORT void ui_progress_spinner_args_set_margin_top(UiProgressbarSpinnerArgs *args, int value); +UIEXPORT void ui_progress_spinner_args_set_margin_bottom(UiProgressbarSpinnerArgs *args, int value); UIEXPORT void ui_progress_spinner_args_set_colspan(UiProgressbarSpinnerArgs *args, int colspan); UIEXPORT void ui_progress_spinner_args_set_rowspan(UiProgressbarSpinnerArgs *args, int rowspan); UIEXPORT void ui_progress_spinner_args_set_name(UiProgressbarSpinnerArgs *args, const char *name); @@ -219,17 +331,22 @@ UIEXPORT void ui_button_args_set_vexpand(UiButtonArgs *args, UiBool value); UIEXPORT void ui_button_args_set_hfill(UiButtonArgs *args, UiBool value); UIEXPORT void ui_button_args_set_vfill(UiButtonArgs *args, UiBool value); UIEXPORT void ui_button_args_set_override_defaults(UiButtonArgs *args, UiBool value); +UIEXPORT void ui_button_args_set_margin(UiButtonArgs *args, int value); +UIEXPORT void ui_button_args_set_margin_left(UiButtonArgs *args, int value); +UIEXPORT void ui_button_args_set_margin_right(UiButtonArgs *args, int value); +UIEXPORT void ui_button_args_set_margin_top(UiButtonArgs *args, int value); +UIEXPORT void ui_button_args_set_margin_bottom(UiButtonArgs *args, int value); UIEXPORT void ui_button_args_set_colspan(UiButtonArgs *args, int colspan); UIEXPORT void ui_button_args_set_rowspan(UiButtonArgs *args, int rowspan); UIEXPORT void ui_button_args_set_name(UiButtonArgs *args, const char *name); UIEXPORT void ui_button_args_set_style_class(UiButtonArgs *args, const char *classname); UIEXPORT void ui_button_args_set_label(UiButtonArgs *args, const char *label); -UIEXPORT void ui_button_args_set_stockid(UiButtonArgs *args, const char *stockid); UIEXPORT void ui_button_args_set_icon(UiButtonArgs *args, const char *icon); +UIEXPORT void ui_button_args_set_tooltip(UiButtonArgs *args, const char *tooltip); UIEXPORT void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype); UIEXPORT void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback); UIEXPORT void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata); -UIEXPORT void ui_button_args_set_groups(UiButtonArgs *args, int *groups); +UIEXPORT void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates); UIEXPORT void ui_button_args_free(UiButtonArgs *args); UIEXPORT UiToggleArgs* ui_toggle_args_new(void); @@ -239,22 +356,54 @@ UIEXPORT void ui_toggle_args_set_vexpand(UiToggleArgs *args, UiBool value); UIEXPORT void ui_toggle_args_set_hfill(UiToggleArgs *args, UiBool value); UIEXPORT void ui_toggle_args_set_vfill(UiToggleArgs *args, UiBool value); UIEXPORT void ui_toggle_args_set_override_defaults(UiToggleArgs *args, UiBool value); +UIEXPORT void ui_toggle_args_set_margin(UiToggleArgs *args, int value); +UIEXPORT void ui_toggle_args_set_margin_left(UiToggleArgs *args, int value); +UIEXPORT void ui_toggle_args_set_margin_right(UiToggleArgs *args, int value); +UIEXPORT void ui_toggle_args_set_margin_top(UiToggleArgs *args, int value); +UIEXPORT void ui_toggle_args_set_margin_bottom(UiToggleArgs *args, int value); UIEXPORT void ui_toggle_args_set_colspan(UiToggleArgs *args, int colspan); UIEXPORT void ui_toggle_args_set_rowspan(UiToggleArgs *args, int rowspan); UIEXPORT void ui_toggle_args_set_name(UiToggleArgs *args, const char *name); UIEXPORT void ui_toggle_args_set_style_class(UiToggleArgs *args, const char *classname); UIEXPORT void ui_toggle_args_set_label(UiToggleArgs *args, const char *label); -UIEXPORT void ui_toggle_args_set_stockid(UiToggleArgs *args, const char *stockid); UIEXPORT void ui_toggle_args_set_icon(UiToggleArgs *args, const char *icon); +UIEXPORT void ui_toggle_args_set_tooltip(UiToggleArgs *args, const char *tooltip); UIEXPORT void ui_toggle_args_set_labeltype(UiToggleArgs *args, int labeltype); UIEXPORT void ui_toggle_args_set_onchange(UiToggleArgs *args, ui_callback callback); UIEXPORT void ui_toggle_args_set_onchangedata(UiToggleArgs *args, void *onchangedata); UIEXPORT void ui_toggle_args_set_varname(UiToggleArgs *args, const char *varname); UIEXPORT void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value); UIEXPORT void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group); -UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *groups); +UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates); UIEXPORT void ui_toggle_args_free(UiToggleArgs *args); +UIEXPORT UiLinkButtonArgs* ui_linkbutton_args_new(void); +UIEXPORT void ui_linkbutton_args_set_fill(UiLinkButtonArgs *args, UiBool fill); +UIEXPORT void ui_linkbutton_args_set_hexpand(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_vexpand(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_hfill(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_vfill(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_override_defaults(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_margin(UiLinkButtonArgs *args, int value); +UIEXPORT void ui_linkbutton_args_set_margin_left(UiLinkButtonArgs *args, int value); +UIEXPORT void ui_linkbutton_args_set_margin_right(UiLinkButtonArgs *args, int value); +UIEXPORT void ui_linkbutton_args_set_margin_top(UiLinkButtonArgs *args, int value); +UIEXPORT void ui_linkbutton_args_set_margin_bottom(UiLinkButtonArgs *args, int value); +UIEXPORT void ui_linkbutton_args_set_colspan(UiLinkButtonArgs *args, int colspan); +UIEXPORT void ui_linkbutton_args_set_rowspan(UiLinkButtonArgs *args, int rowspan); +UIEXPORT void ui_linkbutton_args_set_name(UiLinkButtonArgs *args, const char *name); +UIEXPORT void ui_linkbutton_args_set_style_class(UiLinkButtonArgs *args, const char *classname); +UIEXPORT void ui_linkbutton_args_set_varname(UiLinkButtonArgs *args, const char *varname); +UIEXPORT void ui_linkbutton_args_set_value(UiLinkButtonArgs *args, UiString *value); +UIEXPORT void ui_linkbutton_args_set_label(UiLinkButtonArgs *args, const char *label); +UIEXPORT void ui_linkbutton_args_set_uri(UiLinkButtonArgs *args, const char *uri); +UIEXPORT void ui_linkbutton_args_set_onclick(UiLinkButtonArgs *args, ui_callback callback); +UIEXPORT void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata); +UIEXPORT void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value); +UIEXPORT void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type); +UIEXPORT void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates); +UIEXPORT void ui_linkbutton_args_free(UiLinkButtonArgs *args); + UIEXPORT UiListArgs* ui_list_args_new(void); UIEXPORT void ui_list_args_set_fill(UiListArgs *args, UiBool fill); UIEXPORT void ui_list_args_set_hexpand(UiListArgs *args, UiBool value); @@ -262,6 +411,11 @@ UIEXPORT void ui_list_args_set_vexpand(UiListArgs *args, UiBool value); UIEXPORT void ui_list_args_set_hfill(UiListArgs *args, UiBool value); UIEXPORT void ui_list_args_set_vfill(UiListArgs *args, UiBool value); UIEXPORT void ui_list_args_set_override_defaults(UiListArgs *args, UiBool value); +UIEXPORT void ui_list_args_set_margin(UiListArgs *args, int value); +UIEXPORT void ui_list_args_set_margin_left(UiListArgs *args, int value); +UIEXPORT void ui_list_args_set_margin_right(UiListArgs *args, int value); +UIEXPORT void ui_list_args_set_margin_top(UiListArgs *args, int value); +UIEXPORT void ui_list_args_set_margin_bottom(UiListArgs *args, int value); UIEXPORT void ui_list_args_set_colspan(UiListArgs *args, int colspan); UIEXPORT void ui_list_args_set_rowspan(UiListArgs *args, int rowspan); UIEXPORT void ui_list_args_set_name(UiListArgs *args, const char *name); @@ -273,6 +427,8 @@ UIEXPORT void ui_list_args_set_static_elements(UiListArgs *args, char **strarray UIEXPORT void ui_list_args_set_getvalue_func(UiListArgs *args, ui_getvaluefunc getvalue); UIEXPORT void ui_list_args_set_getvalue_func2(UiListArgs *args, ui_getvaluefunc2 getvalue); UIEXPORT void ui_list_args_set_getvalue_data(UiListArgs *args, void *userdata); +UIEXPORT void ui_list_args_set_getstyle_func(UiListArgs *args, ui_getstylefunc getstyle); +UIEXPORT void ui_list_args_set_getstyle_data(UiListArgs *args, void *userdata); UIEXPORT void ui_list_args_set_onactivate(UiListArgs *args, ui_callback callback); UIEXPORT void ui_list_args_set_onactivatedata(UiListArgs *args, void *userdata); UIEXPORT void ui_list_args_set_onselection(UiListArgs *args, ui_callback callback); @@ -283,9 +439,11 @@ UIEXPORT void ui_list_args_set_ondragcomplete(UiListArgs *args, ui_callback call UIEXPORT void ui_list_args_set_ondragcompletedata(UiListArgs *args, void *userdata); UIEXPORT void ui_list_args_set_ondrop(UiListArgs *args, ui_callback callback); UIEXPORT void ui_list_args_set_ondropdata(UiListArgs *args, void *userdata); +UIEXPORT void ui_list_args_set_onsave(UiListArgs *args, ui_list_savefunc onsave); +UIEXPORT void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata); UIEXPORT void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection); UIEXPORT void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder); -UIEXPORT void ui_list_args_set_groups(UiListArgs *args, int *groups); +UIEXPORT void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates); UIEXPORT void ui_list_args_free(UiListArgs *args); UIEXPORT UiSourceListArgs* ui_sourcelist_args_new(void); @@ -295,6 +453,11 @@ UIEXPORT void ui_sourcelist_args_set_vexpand(UiSourceListArgs *args, UiBool valu UIEXPORT void ui_sourcelist_args_set_hfill(UiSourceListArgs *args, UiBool value); UIEXPORT void ui_sourcelist_args_set_vfill(UiSourceListArgs *args, UiBool value); UIEXPORT void ui_sourcelist_args_set_override_defaults(UiSourceListArgs *args, UiBool value); +UIEXPORT void ui_sourcelist_args_set_margin(UiSourceListArgs *args, int value); +UIEXPORT void ui_sourcelist_args_set_margin_left(UiSourceListArgs *args, int value); +UIEXPORT void ui_sourcelist_args_set_margin_right(UiSourceListArgs *args, int value); +UIEXPORT void ui_sourcelist_args_set_margin_top(UiSourceListArgs *args, int value); +UIEXPORT void ui_sourcelist_args_set_margin_bottom(UiSourceListArgs *args, int value); UIEXPORT void ui_sourcelist_args_set_colspan(UiSourceListArgs *args, int colspan); UIEXPORT void ui_sourcelist_args_set_rowspan(UiSourceListArgs *args, int rowspan); UIEXPORT void ui_sourcelist_args_set_name(UiSourceListArgs *args, const char *name); @@ -308,6 +471,8 @@ UIEXPORT void ui_sourcelist_args_set_onactivate(UiSourceListArgs *args, ui_callb UIEXPORT void ui_sourcelist_args_set_onactivatedata(UiSourceListArgs *args, void *userdata); UIEXPORT void ui_sourcelist_args_set_onbuttonclick(UiSourceListArgs *args, ui_callback callback); UIEXPORT void ui_sourcelist_args_set_onbuttonclickdata(UiSourceListArgs *args, void *userdata); +UIEXPORT void ui_sourcelist_args_set_contextmenu(UiSourceListArgs *args, UiMenuBuilder *menubuilder); +UIEXPORT void ui_sourcelist_args_set_header_is_item(UiSourceListArgs *args, UiBool value); UIEXPORT void ui_sourcelist_args_free(UiSourceListArgs *args); UIEXPORT UiTextAreaArgs* ui_textarea_args_new(void); @@ -317,6 +482,11 @@ UIEXPORT void ui_textarea_args_set_vexpand(UiTextAreaArgs *args, UiBool value); UIEXPORT void ui_textarea_args_set_hfill(UiTextAreaArgs *args, UiBool value); UIEXPORT void ui_textarea_args_set_vfill(UiTextAreaArgs *args, UiBool value); UIEXPORT void ui_textarea_args_set_override_defaults(UiTextAreaArgs *args, UiBool value); +UIEXPORT void ui_textarea_args_set_margin(UiTextAreaArgs *args, int value); +UIEXPORT void ui_textarea_args_set_margin_left(UiTextAreaArgs *args, int value); +UIEXPORT void ui_textarea_args_set_margin_right(UiTextAreaArgs *args, int value); +UIEXPORT void ui_textarea_args_set_margin_top(UiTextAreaArgs *args, int value); +UIEXPORT void ui_textarea_args_set_margin_bottom(UiTextAreaArgs *args, int value); UIEXPORT void ui_textarea_args_set_colspan(UiTextAreaArgs *args, int colspan); UIEXPORT void ui_textarea_args_set_rowspan(UiTextAreaArgs *args, int rowspan); UIEXPORT void ui_textarea_args_set_name(UiTextAreaArgs *args, const char *name); @@ -325,7 +495,7 @@ UIEXPORT void ui_textarea_args_set_onchange(UiTextAreaArgs *args, ui_callback ca UIEXPORT void ui_textarea_args_set_onchangedata(UiTextAreaArgs *args, void *onchangedata); UIEXPORT void ui_textarea_args_set_varname(UiTextAreaArgs *args, const char *varname); UIEXPORT void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value); -UIEXPORT void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *groups); +UIEXPORT void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates); UIEXPORT void ui_textarea_args_free(UiTextAreaArgs *args); UIEXPORT UiTextFieldArgs* ui_textfield_args_new(void); @@ -335,6 +505,11 @@ UIEXPORT void ui_textfield_args_set_vexpand(UiTextFieldArgs *args, UiBool value) UIEXPORT void ui_textfield_args_set_hfill(UiTextFieldArgs *args, UiBool value); UIEXPORT void ui_textfield_args_set_vfill(UiTextFieldArgs *args, UiBool value); UIEXPORT void ui_textfield_args_set_override_defaults(UiTextFieldArgs *args, UiBool value); +UIEXPORT void ui_textfield_args_set_margin(UiTextFieldArgs *args, int value); +UIEXPORT void ui_textfield_args_set_margin_left(UiTextFieldArgs *args, int value); +UIEXPORT void ui_textfield_args_set_margin_right(UiTextFieldArgs *args, int value); +UIEXPORT void ui_textfield_args_set_margin_top(UiTextFieldArgs *args, int value); +UIEXPORT void ui_textfield_args_set_margin_bottom(UiTextFieldArgs *args, int value); UIEXPORT void ui_textfield_args_set_colspan(UiTextFieldArgs *args, int colspan); UIEXPORT void ui_textfield_args_set_rowspan(UiTextFieldArgs *args, int rowspan); UIEXPORT void ui_textfield_args_set_name(UiTextFieldArgs *args, const char *name); @@ -345,9 +520,38 @@ UIEXPORT void ui_textfield_args_set_onactivate(UiTextFieldArgs *args, ui_callbac UIEXPORT void ui_textfield_args_set_onactivatedata(UiTextFieldArgs *args, void *onactivatedata); UIEXPORT void ui_textfield_args_set_varname(UiTextFieldArgs *args, const char *varname); UIEXPORT void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value); -UIEXPORT void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *groups); +UIEXPORT void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates); UIEXPORT void ui_textfield_args_free(UiTextFieldArgs *args); +UIEXPORT UiSpinBoxArgs* ui_spinbox_args_new(void); +UIEXPORT void ui_spinbox_args_set_fill(UiSpinBoxArgs *args, UiBool fill); +UIEXPORT void ui_spinbox_args_set_hexpand(UiSpinBoxArgs *args, UiBool value); +UIEXPORT void ui_spinbox_args_set_vexpand(UiSpinBoxArgs *args, UiBool value); +UIEXPORT void ui_spinbox_args_set_hfill(UiSpinBoxArgs *args, UiBool value); +UIEXPORT void ui_spinbox_args_set_vfill(UiSpinBoxArgs *args, UiBool value); +UIEXPORT void ui_spinbox_args_set_override_defaults(UiSpinBoxArgs *args, UiBool value); +UIEXPORT void ui_spinbox_args_set_margin(UiSpinBoxArgs *args, int value); +UIEXPORT void ui_spinbox_args_set_margin_left(UiSpinBoxArgs *args, int value); +UIEXPORT void ui_spinbox_args_set_margin_right(UiSpinBoxArgs *args, int value); +UIEXPORT void ui_spinbox_args_set_margin_top(UiSpinBoxArgs *args, int value); +UIEXPORT void ui_spinbox_args_set_margin_bottom(UiSpinBoxArgs *args, int value); +UIEXPORT void ui_spinbox_args_set_colspan(UiSpinBoxArgs *args, int colspan); +UIEXPORT void ui_spinbox_args_set_rowspan(UiSpinBoxArgs *args, int rowspan); +UIEXPORT void ui_spinbox_args_set_name(UiSpinBoxArgs *args, const char *name); +UIEXPORT void ui_spinbox_args_set_style_class(UiSpinBoxArgs *args, const char *classname); +UIEXPORT void ui_spinbox_args_set_onchange(UiSpinBoxArgs *args, ui_callback callback); +UIEXPORT void ui_spinbox_args_set_onchangedata(UiSpinBoxArgs *args, void *onchangedata); +UIEXPORT void ui_spinbox_args_set_min(UiSpinBoxArgs *args, double min); +UIEXPORT void ui_spinbox_args_set_max(UiSpinBoxArgs *args, double max); +UIEXPORT void ui_spinbox_args_set_step(UiSpinBoxArgs *args, double step); +UIEXPORT void ui_spinbox_args_set_digits(UiSpinBoxArgs *args, int digits); +UIEXPORT void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varname); +UIEXPORT void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value); +UIEXPORT void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value); +UIEXPORT void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value); +UIEXPORT void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates); +UIEXPORT void ui_spinbox_args_free(UiSpinBoxArgs *args); + UIEXPORT UiWebviewArgs* ui_webview_args_new(void); UIEXPORT void ui_webview_args_set_fill(UiWebviewArgs *args, UiBool fill); UIEXPORT void ui_webview_args_set_hexpand(UiWebviewArgs *args, UiBool value); @@ -355,13 +559,18 @@ UIEXPORT void ui_webview_args_set_vexpand(UiWebviewArgs *args, UiBool value); UIEXPORT void ui_webview_args_set_hfill(UiWebviewArgs *args, UiBool value); UIEXPORT void ui_webview_args_set_vfill(UiWebviewArgs *args, UiBool value); UIEXPORT void ui_webview_args_set_override_defaults(UiWebviewArgs *args, UiBool value); +UIEXPORT void ui_webview_args_set_margin(UiWebviewArgs *args, int value); +UIEXPORT void ui_webview_args_set_margin_left(UiWebviewArgs *args, int value); +UIEXPORT void ui_webview_args_set_margin_right(UiWebviewArgs *args, int value); +UIEXPORT void ui_webview_args_set_margin_top(UiWebviewArgs *args, int value); +UIEXPORT void ui_webview_args_set_margin_bottom(UiWebviewArgs *args, int value); UIEXPORT void ui_webview_args_set_colspan(UiWebviewArgs *args, int colspan); UIEXPORT void ui_webview_args_set_rowspan(UiWebviewArgs *args, int rowspan); UIEXPORT void ui_webview_args_set_name(UiWebviewArgs *args, const char *name); UIEXPORT void ui_webview_args_set_style_class(UiWebviewArgs *args, const char *classname); UIEXPORT void ui_webview_args_set_varname(UiWebviewArgs *args, const char *varname); UIEXPORT void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value); -UIEXPORT void ui_webview_args_set_groups(UiWebviewArgs *args, int *groups); +UIEXPORT void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates); UIEXPORT void ui_webview_args_free(UiWebviewArgs *args); #ifdef __cplusplus diff --git a/ui/common/container.c b/ui/common/container.c new file mode 100644 index 0000000..6f0dde9 --- /dev/null +++ b/ui/common/container.c @@ -0,0 +1,87 @@ +/* + * 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 "container.h" +#include "object.h" + +void ui_end_new(UiObject *obj) { + if(!obj->container_end) { + return; + } + UiContainerX *rm = obj->container_end; + uic_object_pop_container(obj); + ui_free(obj->ctx, rm); +} + +void ui_newline(UiObject *obj) { + UiContainerX *container = obj->container_end; + if(container) { + container->newline = TRUE; + } +} + +void uic_layout_setup_expand_fill( + UiLayout *layout, + UiBool def_hexpand, + UiBool def_vexpand, + UiBool def_hfill, + UiBool def_vfill) +{ + if(layout->fill) { + layout->hfill = TRUE; + layout->vfill = TRUE; + layout->hexpand = TRUE; + layout->vexpand = TRUE; + return; + } + + if(!layout->override_defaults) { + if(def_hexpand) { + layout->hexpand = TRUE; + } + if(def_hfill) { + layout->hfill = TRUE; + } + if(def_vexpand) { + layout->vexpand = TRUE; + } + if(def_vfill) { + layout->vfill = TRUE; + } + } +} + +void uic_layout_setup_margin(UiLayout *layout) { + int margin = layout->margin; + if(margin > 0) { + layout->margin_left = margin; + layout->margin_right = margin; + layout->margin_top = margin; + layout->margin_bottom = margin; + } +} diff --git a/ui/common/container.h b/ui/common/container.h new file mode 100644 index 0000000..6f102ed --- /dev/null +++ b/ui/common/container.h @@ -0,0 +1,59 @@ +/* + * 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 UIC_CONTAINER_H +#define UIC_CONTAINER_H + +#include "../ui/container.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * prepares the layout horizontal and vertical fill/expand settings + * based on fill and defaults + */ +void uic_layout_setup_expand_fill( + UiLayout *layout, + UiBool def_hexpand, + UiBool def_vexpand, + UiBool def_hfill, + UiBool def_vfill); + +/* + * adjusts margin_* if margin > 0 + */ +void uic_layout_setup_margin(UiLayout *layout); + +#ifdef __cplusplus +} +#endif + +#endif /* UIC_CONTAINER_H */ + diff --git a/ui/common/context.c b/ui/common/context.c index d0cd53d..73911a5 100644 --- a/ui/common/context.c +++ b/ui/common/context.c @@ -38,6 +38,7 @@ #include "context.h" #include "../ui/window.h" +#include "../ui/widget.h" #include "document.h" #include "types.h" @@ -102,21 +103,19 @@ void uic_context_attach_document(UiContext *ctx, void *document) { UiContext *doc_ctx = ui_document_context(document); doc_ctx->parent = ctx; - // check if any parent context has an unbound variable with the same name - // as any document variable + // if a document variable has the same name as a parent variable, + // move the bindings to the document UiContext *var_ctx = ctx; while(var_ctx) { - if(var_ctx->vars_unbound && cxMapSize(var_ctx->vars_unbound) > 0) { - CxMapIterator i = cxMapIterator(var_ctx->vars_unbound); - cx_foreach(CxMapEntry*, entry, i) { - printf("attach %s\n", entry->key->data); - UiVar *var = entry->value; - UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key); - if(docvar) { - // bind var to document var - uic_copy_binding(var, docvar, TRUE); - cxIteratorFlagRemoval(i); - } + CxMapIterator i = cxMapIterator(var_ctx->vars); + cx_foreach(CxMapEntry*, entry, i) { + printf("attach %.*s\n", (int)entry->key->len, (char*)entry->key->data); + UiVar *var = entry->value; + UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key); + if(docvar) { + // bind var to document var + uic_copy_binding(var, docvar, TRUE); + cxIteratorFlagRemoval(i); } } @@ -129,10 +128,10 @@ static void uic_context_unbind_vars(UiContext *ctx) { cx_foreach(CxMapEntry*, entry, mi) { UiVar *var = entry->value; // var->from && var->from_ctx && var->from_ctx != ctx + uic_save_var(var); if(var->from) { - uic_save_var2(var); uic_copy_binding(var, var->from, FALSE); - cxMapPut(var->from->from_ctx->vars_unbound, *entry->key, var->from); + cxMapPut(var->from->from_ctx->vars, *entry->key, var->from); var->from = NULL; } } @@ -199,13 +198,6 @@ UiVar* uic_get_var(UiContext *ctx, const char *name) { } UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { - if(ctx->vars_unbound) { - UiVar *unbound = cxMapGet(ctx->vars_unbound, name); - if(unbound) { - return unbound; - } - } - UiVar *var = uic_get_var(ctx, name); if(var) { if(var->type == type) { @@ -224,10 +216,7 @@ UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var); - if(!ctx->vars_unbound) { - ctx->vars_unbound = cxHashMapCreate(ctx->allocator, CX_STORE_POINTERS, 16); - } - cxMapPut(ctx->vars_unbound, name, var); + cxMapPut(ctx->vars, name, var); return var; } @@ -278,7 +267,7 @@ void* uic_create_value(UiContext *ctx, UiVarType type) { } -UiVar* uic_widget_var(UiContext* toplevel, UiContext* current, void* value, const char* varname, UiVarType type) { +UiVar* uic_widget_var(UiContext *toplevel, UiContext *current, void *value, const char *varname, UiVarType type) { if (value) { return uic_create_value_var(current, value); } @@ -383,7 +372,7 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { ui_setop_enable(FALSE); } -void uic_save_var2(UiVar *var) { +void uic_save_var(UiVar *var) { switch(var->type) { case UI_VAR_SPECIAL: break; case UI_VAR_INTEGER: uic_int_save(var->value); break; @@ -542,6 +531,9 @@ void uic_check_group_widgets(UiContext *ctx) { } void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) { + if(enable == NULL) { + enable = (ui_enablefunc)ui_set_enabled; + } // get groups CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); va_list ap; @@ -557,6 +549,22 @@ void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, cxListFree(groups); } +void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups) { + if(enable == NULL) { + enable = (ui_enablefunc)ui_set_enabled; + } + CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), ngroups); + for(int i=0;i= 0;i++) { } @@ -614,7 +622,7 @@ void* ui_realloc(UiContext *ctx, void *ptr, size_t size) { return ctx ? cxRealloc(ctx->allocator, ptr, size) : NULL; } -UIEXPORT char* ui_strdup(UiContext *ctx, const char *str) { +char* ui_strdup(UiContext *ctx, const char *str) { if(!ctx) { return NULL; } @@ -622,3 +630,11 @@ UIEXPORT char* ui_strdup(UiContext *ctx, const char *str) { cxmutstr d = cx_strdup_a(ctx->allocator, s); return d.ptr; } + +void ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr) { + cxMempoolRegister(ctx->mp, data, (cx_destructor_func)destr); +} + +void ui_set_destructor(void *mem, ui_destructor_func destr) { + cxMempoolSetDestructor(mem, (cx_destructor_func)destr); +} diff --git a/ui/common/context.h b/ui/common/context.h index 4fdcfe6..f517090 100644 --- a/ui/common/context.h +++ b/ui/common/context.h @@ -67,8 +67,7 @@ struct UiContext { void *document; CxList *documents; - CxMap *vars; // manually created context vars - CxMap *vars_unbound; // unbound vars created by widgets + CxMap *vars; CxList *groups; // int list CxList *group_widgets; // UiGroupWidget list @@ -92,7 +91,6 @@ struct UiContext { void *close_data; }; -// UiVar replacement, rename it to UiVar when finished struct UiVar { void *value; void *original_value; @@ -133,10 +131,10 @@ 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); -UiVar* uic_widget_var(UiContext* toplevel, UiContext* current, void* value, const char* varname, UiVarType type); +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_save_var2(UiVar *var); +void uic_save_var(UiVar *var); void uic_unbind_var(UiVar *var); void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value); diff --git a/ui/common/document.c b/ui/common/document.c index bd69073..3eb5874 100644 --- a/ui/common/document.c +++ b/ui/common/document.c @@ -36,22 +36,16 @@ #include -static CxMap *documents; -void uic_docmgr_init() { - if (!documents) { - documents = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); - } -} void* ui_document_new(size_t size) { CxMempool *mp = cxMempoolCreateSimple(256); const CxAllocator *a = mp->allocator; UiContext *ctx = uic_context(NULL, mp); - void *document = cxCalloc(a, size, 1); - cxMapPut(documents, cx_hash_key(&document, sizeof(void*)), ctx); - return document; + UiDoc *document = cxCalloc(a, sizeof(UiDoc) + size, 1); + document->ctx = ctx; + return &document->doc; } void ui_document_destroy(void *doc) { @@ -74,7 +68,9 @@ void ui_document_destroy(void *doc) { UiContext* ui_document_context(void *doc) { if(doc) { - return cxMapGet(documents, cx_hash_key(&doc, sizeof(void*))); + char *docPtr = doc; + UiDoc *document = (UiDoc*)(docPtr - sizeof(void*)); + return document->ctx; } else { return NULL; } diff --git a/ui/common/document.h b/ui/common/document.h index 0f439d5..b98c810 100644 --- a/ui/common/document.h +++ b/ui/common/document.h @@ -36,7 +36,11 @@ extern "C" { #endif -void uic_docmgr_init(); +typedef struct UiDoc { + UiContext *ctx; + char doc[]; +} UiDoc; + void uic_document_addvar(void *doc, char *name, int type, size_t vs); diff --git a/ui/common/menu.c b/ui/common/menu.c index 31f8171..b0cfc46 100644 --- a/ui/common/menu.c +++ b/ui/common/menu.c @@ -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; @@ -100,7 +116,7 @@ void ui_menu_create(const char *label) { menu->item.next = NULL; menu->item.type = UI_MENU; - menu->label = label; + menu->label = nl_strdup(label); menu->items_begin = NULL; menu->items_end = NULL; menu->parent = NULL; @@ -133,7 +149,6 @@ void ui_menuitem_create(UiMenuItemArgs *args) { item->item.type = UI_MENU_ITEM; item->label = nl_strdup(args->label); - item->stockid = nl_strdup(args->stockid); item->icon = nl_strdup(args->icon); item->userdata = args->onclickdata; item->callback = args->onclick; @@ -160,7 +175,6 @@ void ui_menu_toggleitem_create(UiMenuToggleItemArgs *args) { item->item.type = UI_MENU_CHECK_ITEM; item->label = nl_strdup(args->label); - item->stockid = nl_strdup(args->stockid); item->icon = nl_strdup(args->icon); item->varname = nl_strdup(args->varname); item->userdata = args->onchangedata; @@ -178,7 +192,6 @@ void ui_menu_radioitem_create(UiMenuToggleItemArgs *args) { item->item.type = UI_MENU_RADIO_ITEM; item->label = nl_strdup(args->label); - item->stockid = nl_strdup(args->stockid); item->icon = nl_strdup(args->icon); item->varname = nl_strdup(args->varname); item->userdata = args->onchangedata; @@ -258,6 +271,7 @@ void ui_contextmenu_builder(UiMenuBuilder **out_builder) { builder->menus_begin = NULL; builder->menus_end = NULL; builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + builder->ref = 1; current_builder = builder; *out_builder = builder; @@ -271,6 +285,7 @@ static void free_menuitem(UiMenuItemI *item) { default: break; case UI_MENU: { UiMenu *menu = (UiMenu*)item; + free(menu->label); UiMenuItemI *m = menu->items_begin; while(m) { UiMenuItemI *next = m->next; @@ -283,7 +298,6 @@ static void free_menuitem(UiMenuItemI *item) { UiMenuItem *i = (UiMenuItem*)item; free(i->groups); free(i->label); - free(i->stockid); free(i->icon); break; } @@ -291,7 +305,6 @@ static void free_menuitem(UiMenuItemI *item) { UiMenuCheckItem *i = (UiMenuCheckItem*)item; free(i->groups); free(i->label); - free(i->stockid); free(i->icon); free(i->varname); break; @@ -300,9 +313,8 @@ static void free_menuitem(UiMenuItemI *item) { UiMenuRadioItem *i = (UiMenuRadioItem*)item; free(i->groups); free(i->label); - free(i->stockid); free(i->icon); - //free(i->varname); + free(i->varname); break; } case UI_MENU_ITEM_LIST: { @@ -329,3 +341,13 @@ void ui_menubuilder_free(UiMenuBuilder *builder) { cxListFree(builder->current); free(builder); } + +void ui_menubuilder_ref(UiMenuBuilder *builder) { + builder->ref++; +} + +void ui_menubuilder_unref(UiMenuBuilder *builder) { + if(--builder->ref <= 0) { + ui_menubuilder_free(builder); + } +} diff --git a/ui/common/menu.h b/ui/common/menu.h index 1ed01b0..f44f6b4 100644 --- a/ui/common/menu.h +++ b/ui/common/menu.h @@ -66,7 +66,7 @@ struct UiMenuItemI { struct UiMenu { UiMenuItemI item; - const char *label; + char *label; UiMenuItemI *items_begin; UiMenuItemI *items_end; UiMenu *parent; @@ -77,7 +77,6 @@ struct UiMenuItem { UiMenuItemI item; ui_callback callback; char *label; - char *stockid; char *icon; void *userdata; int *groups; @@ -87,7 +86,6 @@ struct UiMenuItem { struct UiMenuCheckItem { UiMenuItemI item; char *label; - char *stockid; char *icon; char *varname; ui_callback callback; @@ -99,7 +97,6 @@ struct UiMenuCheckItem { struct UiMenuRadioItem { UiMenuItemI item; char *label; - char *stockid; char *icon; char *varname; ui_callback callback; @@ -123,6 +120,7 @@ struct UiMenuBuilder { UiMenu *menus_begin; UiMenu *menus_end; CxList *current; + int ref; }; void uic_menu_init(void); @@ -133,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 diff --git a/ui/common/object.c b/ui/common/object.c index e7981c4..b041bc9 100644 --- a/ui/common/object.c +++ b/ui/common/object.c @@ -28,7 +28,6 @@ #include #include - #include "object.h" #include "context.h" @@ -74,32 +73,6 @@ void uic_object_destroyed(UiObject *obj) { } } -void ui_end(UiObject *obj) { - if(!obj->next) { - return; - } - - UiObject *prev = NULL; - while(obj->next) { - prev = obj; - obj = obj->next; - } - - if(prev) { - // TODO: free last obj - prev->next = NULL; - } -} - -void ui_end_new(UiObject *obj) { - if(!obj->container_end) { - return; - } - UiContainerX *rm = obj->container_end; - uic_object_pop_container(obj); - ui_free(obj->ctx, rm); -} - void ui_object_ref(UiObject *obj) { obj->ref++; } @@ -133,17 +106,18 @@ void uic_object_destroy(UiObject *obj) { } UiObject* uic_object_new_toplevel(void) { + fflush(stdout); CxMempool *mp = cxMempoolCreateSimple(256); UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); + fflush(stdout); obj->ctx = uic_context(obj, mp); + obj->ctx->parent = ui_global_context(); + fflush(stdout); uic_object_created(obj); + fflush(stdout); return obj; } -UiObject* uic_object_new(UiObject *toplevel, UIWIDGET widget) { - return uic_ctx_object_new(toplevel->ctx, widget); -} - UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget) { UiObject *newobj = cxCalloc(ctx->allocator, 1, sizeof(UiObject)); newobj->ctx = ctx; @@ -152,26 +126,6 @@ UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget) { return newobj; } -void uic_obj_add(UiObject *toplevel, UiObject *ctobj) { - UiObject *current = uic_current_obj(toplevel); - current->next = ctobj; -} - -UiObject* uic_current_obj(UiObject *toplevel) { - if(!toplevel) { - return NULL; - } - UiObject *obj = toplevel; - while(obj->next) { - obj = obj->next; - } - return obj; -} - -UiContainer* uic_get_current_container(UiObject *obj) { - return uic_current_obj(obj)->container; -} - void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer) { newcontainer->prev = toplevel->container_end; if(toplevel->container_end) { @@ -190,4 +144,29 @@ void uic_object_pop_container(UiObject *toplevel) { } else { toplevel->container_begin = NULL; } + + // TODO: free container? +} + +/* + * This might look like a weird function, but in case a container creates a + * sub-container, 2 container objects are added to the list, however we want + * only one container, otherwise ui_container_finish() would not work + */ +void uic_object_remove_second_last_container(UiObject *toplevel) { + if(toplevel->container_end && toplevel->container_end->prev) { + UiContainerX *end = toplevel->container_end; + UiContainerX *rm = toplevel->container_end->prev; + + end->prev = rm->prev; + if(rm->prev) { + rm->prev->next = end; + } else { + toplevel->container_begin = end; + } + + // TODO: free container? + } else { + fprintf(stderr, "Error: uic_object_remove_second_last_container expected at least 2 containers\n"); + } } diff --git a/ui/common/object.h b/ui/common/object.h index 45cf683..e6dd3c3 100644 --- a/ui/common/object.h +++ b/ui/common/object.h @@ -48,13 +48,10 @@ void uic_object_destroy(UiObject *obj); UiObject* uic_object_new_toplevel(void); UiObject* uic_object_new(UiObject *toplevel, UIWIDGET widget); UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget); -void uic_obj_add(UiObject *toplevel, UiObject *ctobj); -UiObject* uic_current_obj(UiObject *toplevel); - -UiContainer* uic_get_current_container(UiObject *obj); // deprecated void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer); void uic_object_pop_container(UiObject *toplevel); +void uic_object_remove_second_last_container(UiObject *toplevel); diff --git a/ui/common/objs.mk b/ui/common/objs.mk index a126b56..4d401b5 100644 --- a/ui/common/objs.mk +++ b/ui/common/objs.mk @@ -32,6 +32,7 @@ COMMON_OBJPRE = $(OBJ_DIR)$(COMMON_SRC_DIR) COMMON_OBJ = context$(OBJ_EXT) COMMON_OBJ += document$(OBJ_EXT) COMMON_OBJ += object$(OBJ_EXT) +COMMON_OBJ += container$(OBJ_EXT) COMMON_OBJ += types$(OBJ_EXT) COMMON_OBJ += properties$(OBJ_EXT) COMMON_OBJ += menu$(OBJ_EXT) diff --git a/ui/common/properties.c b/ui/common/properties.c index 629112f..28676fb 100644 --- a/ui/common/properties.c +++ b/ui/common/properties.c @@ -73,7 +73,7 @@ char* ui_getappdir(void) { #define UI_ENV_HOME "USERPROFILE" #endif -char* ui_configfile(char *name) { +char* ui_configfile(const char *name) { const char *appname = ui_appname(); if(!appname) { return NULL; @@ -128,6 +128,7 @@ static int ui_mkdir(char *path) { void uic_load_app_properties() { application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128); + application_properties->collection.simple_destructor = free; if(!ui_appname()) { // applications without name cannot load app properties @@ -191,13 +192,22 @@ int uic_store_app_properties() { return ret; } +// public +int ui_app_save_settings(void) { + return uic_store_app_properties(); +} + const char* ui_get_property(const char *name) { return cxMapGet(application_properties, name); } void ui_set_property(const char *name, const char *value) { - cxMapPut(application_properties, name, (char*)value); + if(value) { + cxMapPut(application_properties, name, strdup(value)); + } else { + cxMapRemove(application_properties, name); + } } const char* ui_set_default_property(const char *name, const char *value) { @@ -236,12 +246,14 @@ static char* uic_concat_path(const char *base, const char *p, const char *ext) { #ifndef UI_COCOA -void ui_locales_dir(char *path) { - locales_dir = path; +void ui_locales_dir(const char *path) { + free(locales_dir); + locales_dir = path ? strdup(path) : NULL; } -void ui_pixmaps_dir(char *path) { - pixmaps_dir = path; +void ui_pixmaps_dir(const char *path) { + free(pixmaps_dir); + pixmaps_dir = path ? strdup(path) : NULL; } char* uic_get_image_path(const char *imgfilename) { @@ -252,7 +264,7 @@ char* uic_get_image_path(const char *imgfilename) { } } -void ui_load_lang(char *locale) { +void ui_load_lang(const char *locale) { if(!locale) { locale = "en_EN"; } @@ -314,12 +326,12 @@ int uic_load_language_file(const char *path) { return 0; } -char* uistr(char *name) { +char* uistr(const char *name) { char *value = uistr_n(name); return value ? value : "missing string"; } -char* uistr_n(char *name) { +char* uistr_n(const char *name) { if(!language) { return NULL; } diff --git a/ui/common/toolbar.c b/ui/common/toolbar.c index 199dc41..f237290 100644 --- a/ui/common/toolbar.c +++ b/ui/common/toolbar.c @@ -29,69 +29,73 @@ #include "toolbar.h" #include "menu.h" +#include #include static CxMap* toolbar_items; -static CxList* toolbar_defaults[3]; // 0: left 1: center 2: right +static CxList* toolbar_defaults[8]; // 0: left 1: center 2: right static UiToolbarMenuItem* ui_appmenu; void uic_toolbar_init(void) { toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); - toolbar_defaults[0] = cxLinkedListCreateSimple(CX_STORE_POINTERS); - toolbar_defaults[1] = cxLinkedListCreateSimple(CX_STORE_POINTERS); - toolbar_defaults[2] = cxLinkedListCreateSimple(CX_STORE_POINTERS); + for(int i=0;i<8;i++) { + toolbar_defaults[i] = cxLinkedListCreateSimple(CX_STORE_POINTERS); + } } static char* nl_strdup(const char* str) { return str ? strdup(str) : NULL; } -static UiToolbarItemArgs itemargs_copy(UiToolbarItemArgs *args, size_t *ngroups) { +static UiToolbarItemArgs itemargs_copy(UiToolbarItemArgs *args, size_t *ngroups, size_t *nvstates) { UiToolbarItemArgs newargs; newargs.label = nl_strdup(args->label); - newargs.stockid = nl_strdup(args->stockid); newargs.icon = nl_strdup(args->icon); + newargs.tooltip = nl_strdup(args->tooltip); newargs.onclick = args->onclick; newargs.onclickdata = args->onclickdata; newargs.groups = uic_copy_groups(args->groups, ngroups); + newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates); return newargs; } void ui_toolbar_item_create(const char* name, UiToolbarItemArgs *args) { UiToolbarItem* item = malloc(sizeof(UiToolbarItem)); item->item.type = UI_TOOLBAR_ITEM; - item->args = itemargs_copy(args, &item->ngroups); + item->args = itemargs_copy(args, &item->ngroups, &item->nvstates); cxMapPut(toolbar_items, name, item); } -static UiToolbarToggleItemArgs toggleitemargs_copy(UiToolbarToggleItemArgs *args, size_t *ngroups) { +static UiToolbarToggleItemArgs toggleitemargs_copy(UiToolbarToggleItemArgs *args, size_t *ngroups, size_t *nvstates) { UiToolbarToggleItemArgs newargs; newargs.label = nl_strdup(args->label); - newargs.stockid = nl_strdup(args->stockid); newargs.icon = nl_strdup(args->icon); + newargs.tooltip = nl_strdup(args->tooltip); newargs.varname = nl_strdup(args->varname); newargs.onchange = args->onchange; newargs.onchangedata = args->onchangedata; newargs.groups = uic_copy_groups(args->groups, ngroups); + newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates); return newargs; } void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs *args) { UiToolbarToggleItem* item = malloc(sizeof(UiToolbarToggleItem)); item->item.type = UI_TOOLBAR_TOGGLEITEM; - item->args = toggleitemargs_copy(args, &item->ngroups); + item->args = toggleitemargs_copy(args, &item->ngroups, &item->nvstates); cxMapPut(toolbar_items, name, item); } -static UiToolbarMenuArgs menuargs_copy(UiToolbarMenuArgs *args) { +static UiToolbarMenuArgs menuargs_copy(UiToolbarMenuArgs *args, size_t *nvstates) { UiToolbarMenuArgs newargs; newargs.label = nl_strdup(args->label); - newargs.stockid = nl_strdup(args->stockid); newargs.icon = nl_strdup(args->icon); + newargs.tooltip = nl_strdup(args->tooltip); + newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates); return newargs; } @@ -99,7 +103,7 @@ UIEXPORT void ui_toolbar_menu_create(const char* name, UiToolbarMenuArgs *args) UiToolbarMenuItem* item = malloc(sizeof(UiToolbarMenuItem)); item->item.type = UI_TOOLBAR_MENU; memset(&item->menu, 0, sizeof(UiMenu)); - item->args = menuargs_copy(args); + item->args = menuargs_copy(args, &item->nvstates); item->end = 0; @@ -120,7 +124,7 @@ CxMap* uic_get_toolbar_items(void) { } CxList* uic_get_toolbar_defaults(enum UiToolbarPos pos) { - if (pos >= 0 && pos < 3) { + if (pos >= 0 && pos < 8) { return toolbar_defaults[pos]; } return NULL; @@ -128,15 +132,19 @@ CxList* uic_get_toolbar_defaults(enum UiToolbarPos pos) { void ui_toolbar_add_default(const char* name, enum UiToolbarPos pos) { char* cp = strdup(name); - if (pos >= 0 && pos < 3) { + if (pos >= 0 && pos < 8) { cxListAdd(toolbar_defaults[pos], cp); } else { - // TODO: error + fprintf(stderr, "Error: illegal toolbar position: %d\n", (int)pos); } } UiBool uic_toolbar_isenabled(void) { - return cxListSize(toolbar_defaults[0]) + cxListSize(toolbar_defaults[1]) + cxListSize(toolbar_defaults[2]) > 0; + size_t size = 0; + for(int i=0;i<8;i++) { + size += cxListSize(toolbar_defaults[i]); + } + return size > 0; } UiToolbarItemI* uic_toolbar_get_item(const char* name) { diff --git a/ui/common/toolbar.h b/ui/common/toolbar.h index a13828e..1ac1d55 100644 --- a/ui/common/toolbar.h +++ b/ui/common/toolbar.h @@ -63,18 +63,21 @@ struct UiToolbarItem { UiToolbarItemI item; UiToolbarItemArgs args; size_t ngroups; + size_t nvstates; }; struct UiToolbarToggleItem { UiToolbarItemI item; UiToolbarToggleItemArgs args; size_t ngroups; + size_t nvstates; }; struct UiToolbarMenuItem { UiToolbarItemI item; UiMenu menu; UiToolbarMenuArgs args; + size_t nvstates; int end; }; diff --git a/ui/common/types.c b/ui/common/types.c index b3f55d1..57ad68e 100644 --- a/ui/common/types.c +++ b/ui/common/types.c @@ -165,12 +165,26 @@ void ui_list_clear(UiList *list) { cxListClear(list->data); } -UIEXPORT void ui_list_update(UiList *list) { +void ui_list_update(UiList *list) { if(list->update) { list->update(list, -1); } } +void ui_list_update_row(UiList *list, int row) { + if(list->update) { + list->update(list, row); + } +} + +UiListSelection ui_list_get_selection(UiList *list) { + if(list->getselection) { + return list->getselection(list); + } else { + return (UiListSelection){0, NULL}; + } +} + void ui_list_addobsv(UiList *list, ui_callback f, void *data) { list->observers = ui_add_observer(list->observers, f, data); } @@ -206,6 +220,7 @@ UiModel* ui_model(UiContext *ctx, ...) { va_end(ap); size_t len = cxListSize(cols); + info->alloc = len; info->columns = len; info->types = ui_calloc(ctx, len, sizeof(UiModelType)); info->titles = ui_calloc(ctx, len, sizeof(char*)); @@ -215,7 +230,7 @@ UiModel* ui_model(UiContext *ctx, ...) { CxIterator iter = cxListIterator(cols); cx_foreach(UiColumn*, c, iter) { info->types[i] = c->type; - info->titles[i] = c->name; + info->titles[i] = ui_strdup(ctx, c->name); i++; } cxListFree(cols); @@ -223,6 +238,30 @@ UiModel* ui_model(UiContext *ctx, ...) { return info; } +#define UI_MODEL_DEFAULT_ALLOC_SIZE 16 + +UiModel* ui_model_new(UiContext *ctx) { + UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel)); + info->alloc = UI_MODEL_DEFAULT_ALLOC_SIZE; + info->types = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(UiModelType)); + info->titles = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(char*)); + info->columnsize = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(int)); + return info; +} + +void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width) { + if(model->columns >= model->alloc) { + model->alloc += UI_MODEL_DEFAULT_ALLOC_SIZE; + model->types = ui_realloc(ctx, model->types, model->alloc * sizeof(UiModelType)); + model->titles = ui_realloc(ctx, model->titles, model->alloc * sizeof(char*)); + model->columnsize = ui_realloc(ctx, model->columnsize, model->alloc * sizeof(int)); + } + model->types[model->columns] = type; + model->titles[model->columns] = ui_strdup(ctx, title); + model->columnsize[model->columns] = width; + model->columns++; +} + UiModel* ui_model_copy(UiContext *ctx, UiModel* model) { const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator; @@ -244,6 +283,9 @@ UiModel* ui_model_copy(UiContext *ctx, UiModel* model) { void ui_model_free(UiContext *ctx, UiModel *mi) { const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator; + for(int i=0;icolumns;i++) { + ui_free(ctx, mi->titles[i]); + } cxFree(a, mi->types); cxFree(a, mi->titles); cxFree(a, mi->columnsize); @@ -271,18 +313,32 @@ UiDouble* ui_double_new(UiContext *ctx, const char *name) { return d; } +static void string_destroy(UiString *s) { + if(s->value.free && s->value.ptr) { + s->value.free(s->value.ptr); + } +} + UiString* ui_string_new(UiContext *ctx, const char *name) { UiString *s = ui_malloc(ctx, sizeof(UiString)); memset(s, 0, sizeof(UiString)); + ui_set_destructor(s, (ui_destructor_func)string_destroy); if(name) { uic_reg_var(ctx, name, UI_VAR_STRING, s); } return s; } +static void text_destroy(UiText *t) { + if(t->destroy) { + t->destroy(t); + } +} + UiText* ui_text_new(UiContext *ctx, const char *name) { UiText *t = ui_malloc(ctx, sizeof(UiText)); memset(t, 0, sizeof(UiText)); + ui_set_destructor(t, (ui_destructor_func)text_destroy); if(name) { uic_reg_var(ctx, name, UI_VAR_TEXT, t); } @@ -298,9 +354,16 @@ UiRange* ui_range_new(UiContext *ctx, const char *name) { return r; } -UIEXPORT UiGeneric* ui_generic_new(UiContext *ctx, const char *name) { +static void generic_destroy(UiGeneric *g) { + if(g->destroy) { + g->destroy(g); + } +} + +UiGeneric* ui_generic_new(UiContext *ctx, const char *name) { UiGeneric *g = ui_malloc(ctx, sizeof(UiGeneric)); memset(g, 0, sizeof(UiGeneric)); + ui_set_destructor(g, (ui_destructor_func)generic_destroy); if(name) { uic_reg_var(ctx, name, UI_VAR_GENERIC, g); } @@ -375,7 +438,7 @@ char* ui_string_get(UiString* s) { return s->get ? s->get(s) : s->value.ptr; } else { - return 0; + return NULL; } } @@ -405,6 +468,69 @@ char* ui_text_get(UiText* s) { return s->get ? s->get(s) : s->value.ptr; } else { + return NULL; + } +} + +void ui_range_set(UiRange *r, double value) { + if (r) { + if (r->set) { + r->set(r, value); + } else { + r->value = value; + } + } +} + +void ui_range_set_range(UiRange *r, double min, double max) { + if (r) { + if (r->setrange) { + r->setrange(r, min, max); + } else { + r->min = min; + r->max = max; + } + } +} + +void ui_range_set_extent(UiRange *r, double extent) { + if (r) { + if (r->setextent) { + r->setextent(r, extent); + } else { + r->extent = extent; + } + } +} + +double ui_range_get(UiRange *r) { + if (r) { + return r->get ? r->get(r) : r->value; + } else { + return 0; + } +} + +double ui_range_get_min(UiRange *r) { + if (r) { + return r->min; + } else { + return 0; + } +} + +double ui_range_get_max(UiRange *r) { + if (r) { + return r->max; + } else { + return 0; + } +} + +double ui_range_get_extent(UiRange *r) { + if (r) { + return r->extent; + } else { return 0; } } @@ -566,6 +692,8 @@ void uic_range_unbind(UiRange *r) { void uic_list_unbind(UiList *l) { l->update = NULL; + l->getselection = NULL; + l->setselection = NULL; l->obj = NULL; } @@ -585,10 +713,12 @@ UIEXPORT UiListSelection ui_list_getselection(UiList *list) { } UIEXPORT void ui_list_setselection(UiList *list, int index) { - if (list->setselection && index >= 0) { - UiListSelection sel; - sel.count = 1; - sel.rows = &index; + if (list->setselection) { + UiListSelection sel = { 0, NULL }; + if(index >= 0) { + sel.count = 1; + sel.rows = &index; + } list->setselection(list, sel); } } diff --git a/ui/common/wrapper.c b/ui/common/wrapper.c index ea88553..50ecf40 100644 --- a/ui/common/wrapper.c +++ b/ui/common/wrapper.c @@ -134,6 +134,17 @@ int ui_srclist_size(UiList *list) { return ui_list_count(list); } +/* + * numerates all sublists and sets the sublist index as userdata + */ +void ui_srclist_generate_sublist_num_data(UiList *list) { + CxList *cxlist = list->data; + CxIterator i = cxListIterator(cxlist); + cx_foreach(UiSubList *, sublist, i) { + sublist->userdata = (void*)i.index; + } +} + /* ---------------------------- UiSubListEventData ---------------------------- */ @@ -211,6 +222,10 @@ void ui_sublist_item_set_button_label(UiSubListItem *item, const char *button_la item->button_label = button_label ? strdup(button_label) : NULL; } +void ui_sublist_item_set_button_menu(UiSubListItem *item, UiMenuBuilder *menu) { + item->button_menu = menu; +} + void ui_sublist_item_set_badge(UiSubListItem *item, const char *badge) { item->badge = badge ? strdup(badge) : NULL; } @@ -218,3 +233,103 @@ void ui_sublist_item_set_badge(UiSubListItem *item, const char *badge) { void ui_sublist_item_set_eventdata(UiSubListItem *item, void *eventdata) { item->eventdata = NULL; } + +/* ---------------------------- UiListSelection ---------------------------- */ + +UiListSelection* ui_list_get_selection_allocated(UiList *list) { + UiListSelection *sel = malloc(sizeof(UiListSelection)); + *sel = ui_list_get_selection(list); + return sel; + +} + +int ui_list_selection_get_count(UiListSelection *sel) { + return sel->count; +} + +int* ui_list_selection_get_rows(UiListSelection *sel) { + return sel->rows; +} + +UIEXPORT void ui_list_set_selected_indices(UiList *list, int *indices, int num) { + UiListSelection sel; + sel.rows = indices; + sel.count = num; + if(list->setselection) { + list->setselection(list, sel); + } +} + +void ui_list_selection_free(UiListSelection *sel) { + ui_listselection_free(*sel); + free(sel); +} + +/* ---------------------------- UiFileList ---------------------------- */ + +int ui_filelist_count(UiFileList *flist) { + return flist->nfiles; +} + +char* ui_filelist_get(UiFileList *flist, int index) { + if(index >= 0 && index < flist->nfiles) { + return flist->files[index]; + } + return NULL; +} + +/* ---------------------------- UiTextStyle ---------------------------- */ + +void ui_textstyle_set_bold(UiTextStyle *style, UiBool set) { + if(set) { + style->text_style |= UI_TEXT_STYLE_BOLD; + } else { + style->text_style &= ~UI_TEXT_STYLE_BOLD; + } +} + +void ui_textstyle_set_underline(UiTextStyle *style, UiBool set) { + if(set) { + style->text_style |= UI_TEXT_STYLE_UNDERLINE; + } else { + style->text_style &= ~UI_TEXT_STYLE_UNDERLINE; + } +} + +void ui_textstyle_set_italic(UiTextStyle *style, UiBool set) { + if(set) { + style->text_style |= UI_TEXT_STYLE_ITALIC; + } else { + style->text_style &= ~UI_TEXT_STYLE_ITALIC; + } +} + +void ui_textstyle_set_color(UiTextStyle *style, int r, int g, int b) { + style->fg_set = TRUE; + style->fg.red = r; + style->fg.green = g; + style->fg.blue = b; +} + +void ui_textstyle_enable_color(UiTextStyle *style, UiBool enable) { + style->fg_set = enable; +} + + +/* ---------------------------- UiCellValue ---------------------------- */ + +UiBool ui_cell_value_is_string(UiCellValue *value) { + return value->type == UI_STRING_EDITABLE; +} + +UiBool ui_cell_value_is_int(UiCellValue *value) { + return FALSE; // TODO +} + +const char* ui_cell_value_get_string(UiCellValue *value) { + return value->string; +} + +int64_t ui_cell_value_get_int(UiCellValue *value) { + return value->i; +} diff --git a/ui/common/wrapper.h b/ui/common/wrapper.h index d190fc8..88fd8c8 100644 --- a/ui/common/wrapper.h +++ b/ui/common/wrapper.h @@ -58,6 +58,7 @@ UIEXPORT void ui_srclist_insert(UiList *list, int index, UiSubList *item); UIEXPORT void ui_srclist_remove(UiList *list, int index); UIEXPORT void ui_srclist_clear(UiList *list); UIEXPORT int ui_srclist_size(UiList *list); +UIEXPORT void ui_srclist_generate_sublist_num_data(UiList *list); UIEXPORT UiList* ui_sublist_event_get_list(UiSubListEventData *event); UIEXPORT int ui_sublist_event_get_sublist_index(UiSubListEventData *event); @@ -74,6 +75,25 @@ UIEXPORT int ui_event_get_eventdatatype(UiEvent *event); UIEXPORT int ui_event_get_int(UiEvent *event); UIEXPORT int ui_event_get_set(UiEvent *event); +UIEXPORT UiListSelection* ui_list_get_selection_allocated(UiList *list); +UIEXPORT int ui_list_selection_get_count(UiListSelection *sel); +UIEXPORT int* ui_list_selection_get_rows(UiListSelection *sel); +UIEXPORT void ui_list_set_selected_indices(UiList *list, int *indices, int num); +UIEXPORT void ui_list_selection_free(UiListSelection *sel); + +UIEXPORT int ui_filelist_count(UiFileList *flist); +UIEXPORT char* ui_filelist_get(UiFileList *flist, int index); + +UIEXPORT void ui_textstyle_set_bold(UiTextStyle *style, UiBool set); +UIEXPORT void ui_textstyle_set_underline(UiTextStyle *style, UiBool set); +UIEXPORT void ui_textstyle_set_italic(UiTextStyle *style, UiBool set); +UIEXPORT void ui_textstyle_set_color(UiTextStyle *style, int r, int g, int b); +UIEXPORT void ui_textstyle_enable_color(UiTextStyle *style, UiBool enable); + +UIEXPORT UiBool ui_cell_value_is_string(UiCellValue *value); +UIEXPORT UiBool ui_cell_value_is_int(UiCellValue *value); +UIEXPORT const char* ui_cell_value_get_string(UiCellValue *value); +UIEXPORT int64_t ui_cell_value_get_int(UiCellValue *value); #ifdef __cplusplus diff --git a/ui/gtk/button.c b/ui/gtk/button.c index 6f6ad0b..6db5141 100644 --- a/ui/gtk/button.c +++ b/ui/gtk/button.c @@ -32,6 +32,8 @@ #include "button.h" #include "container.h" #include +#include +#include #include "../common/context.h" #include "../common/object.h" @@ -58,6 +60,7 @@ GtkWidget* ui_create_button( UiObject *obj, const char *label, const char *icon, + const char *tooltip, ui_callback onclick, void *userdata, int event_value, @@ -65,6 +68,9 @@ GtkWidget* ui_create_button( { GtkWidget *button = gtk_button_new_with_label(label); ui_button_set_icon_name(button, icon); + if(tooltip) { + gtk_widget_set_tooltip_text(button, tooltip); + } if(onclick) { UiEventData *event = malloc(sizeof(UiEventData)); @@ -98,12 +104,12 @@ GtkWidget* ui_create_button( } UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) { - UiObject* current = uic_current_obj(obj); - GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->onclick, args->onclickdata, 0, FALSE); + GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->tooltip, args->onclick, args->onclickdata, 0, FALSE); ui_set_name_and_style(button, args->name, args->style_class); ui_set_widget_groups(obj->ctx, button, args->groups); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, button); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, button, &layout); return button; } @@ -120,6 +126,14 @@ void ui_button_clicked(GtkWidget *widget, UiEventData *event) { event->callback(&e, event->userdata); } +void ui_button_set_label(UIWIDGET button, const char *label) { + gtk_button_set_label(GTK_BUTTON(button), label); +} + +void ui_button_set_icon(UIWIDGET button, const char *icon) { + ui_button_set_icon_name(button, icon); +} + int64_t ui_toggle_button_get(UiInteger *integer) { GtkToggleButton *button = integer->obj; integer->value = (int)gtk_toggle_button_get_active(button); @@ -171,6 +185,7 @@ void ui_setup_togglebutton( GtkWidget *togglebutton, const char *label, const char *icon, + const char *tooltip, const char *varname, UiInteger *value, ui_callback onchange, @@ -181,6 +196,9 @@ void ui_setup_togglebutton( gtk_button_set_label(GTK_BUTTON(togglebutton), label); } ui_button_set_icon_name(togglebutton, icon); + if(tooltip) { + gtk_widget_set_tooltip_text(togglebutton, tooltip); + } ui_bind_togglebutton( obj, @@ -210,8 +228,7 @@ void ui_bind_togglebutton( void (*enable_state_func)(void*, void*), int enable_state) { - UiObject* current = uic_current_obj(obj); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, value, varname, UI_VAR_INTEGER); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER); if (var) { UiInteger* value = (UiInteger*)var->value; value->obj = widget; @@ -281,13 +298,12 @@ void ui_bind_togglebutton( } static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleArgs *args) { - UiObject* current = uic_current_obj(obj); - ui_setup_togglebutton( obj, widget, args->label, args->icon, + args->tooltip, args->varname, args->value, args->onchange, @@ -296,8 +312,9 @@ static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleAr ui_set_name_and_style(widget, args->name, args->style_class); ui_set_widget_groups(obj->ctx, widget, args->groups); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, widget); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); return widget; } @@ -340,9 +357,7 @@ static void ui_checkbox_enable_state(GtkCheckButton *widget, UiEventData *event) } } -UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) { - UiObject* current = uic_current_obj(obj); - +UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) { GtkWidget *widget = gtk_check_button_new_with_label(args->label); ui_bind_togglebutton( obj, @@ -360,8 +375,9 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) { ui_set_name_and_style(widget, args->name, args->style_class); ui_set_widget_groups(obj->ctx, widget, args->groups); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, widget); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); return widget; } @@ -372,14 +388,99 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) { } #endif + +#if GTK_MAJOR_VERSION >= 3 + +static void switch_changed( + GObject *gobject, + GParamSpec *pspec, + UiVarEventData *event) +{ + GtkSwitch *sw = GTK_SWITCH (gobject); + gboolean active = gtk_switch_get_active (sw); + + UiEvent e; + e.obj = event->obj; + e.document = e.obj->ctx->document; + e.window = e.obj->window; + e.eventdata = NULL; + e.eventdatatype = 0; + e.set = ui_get_setop(); + + if(event->callback) { + event->callback(&e, event->userdata); + } + if(event->var) { + UiInteger *i = event->var->value; + ui_notify_evt(i->observers, &e); + } +} + UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) { -#ifdef UI_GTK3 - return NULL; // TODO + GtkWidget *widget = gtk_switch_new(); + ui_set_name_and_style(widget, args->name, args->style_class); + ui_set_widget_groups(obj->ctx, widget, args->groups); + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); + if(var) { + UiInteger *value = var->value; + value->obj = widget; + value->get = ui_switch_get; + value->set = ui_switch_set; + + if(value->value) { + gtk_switch_set_active(GTK_SWITCH(widget), TRUE); + } + + + } + + UiVarEventData *event = malloc(sizeof(UiVarEventData)); + event->obj = obj; + event->callback = args->onchange; + event->userdata = args->onchangedata; + event->var = var; + event->observers = NULL; + + g_signal_connect( + widget, + "notify::active", + G_CALLBACK(switch_changed), + event); + + g_signal_connect( + widget, + "destroy", + G_CALLBACK(ui_destroy_vardata), + event); + + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); + + return widget; +} + +int64_t ui_switch_get(UiInteger *value) { + GtkSwitch *sw = GTK_SWITCH((GtkWidget*)value->obj); + value->value = gtk_switch_get_active(sw); + return value->value; +} + +void ui_switch_set(UiInteger *value, int64_t i) { + GtkSwitch *sw = GTK_SWITCH((GtkWidget*)value->obj); + value->value = i; + gtk_switch_set_active(sw, i); +} + #else + +UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) { return ui_checkbox_create(obj, args); -#endif } +#endif + #if GTK_MAJOR_VERSION >= 4 #define RADIOBUTTON_NEW(group, label) gtk_check_button_new_with_label(label) #define RADIOBUTTON_SET_GROUP(button, group) @@ -424,12 +525,10 @@ static void destroy_radiobutton(GtkWidget *w, UiRadioButtonData *data) { } UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) { - UiObject* current = uic_current_obj(obj); - GSList *rg = NULL; UiInteger *rgroup; - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_INTEGER); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); UiBool first = FALSE; if(var) { @@ -505,8 +604,9 @@ UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) { event); } - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, rbutton); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, rbutton, &layout); return rbutton; } @@ -594,3 +694,307 @@ void ui_radiobutton_set(UiInteger *value, int64_t i) { value->value = i; } #endif + + +static void ui_destroy_linkbutton(GtkWidget *widget, UiLinkButton *data) { + free(data->link); + free(data); +} + +static const char* linkbutton_get_uri(UiLinkButton *link) { + if(link->type == UI_LINK_BUTTON) { + return link->link; + } else { + return gtk_link_button_get_uri(GTK_LINK_BUTTON(link->widget)); + } +} + +static void linkbutton_set_uri(UiLinkButton *link, const char *uri) { + if(link->type == UI_LINK_BUTTON) { + free(link->link); + link->link = uri ? strdup(uri) : NULL; + } else { + gtk_link_button_set_uri(GTK_LINK_BUTTON(link->widget), uri); + } +} + +static gboolean linkbutton_get_visited(UiLinkButton *link) { + if(link->type == UI_LINK_BUTTON) { + return FALSE; + } else { + gtk_link_button_get_visited(GTK_LINK_BUTTON(link->widget)); + } +} + +static void linkbutton_set_visited(UiLinkButton *link, gboolean visited) { + if(link->type != UI_LINK_BUTTON) { + gtk_link_button_set_visited(GTK_LINK_BUTTON(link->widget), visited); + } +} + +/* + * Apply linkbutton settings from json. Expects jsonvalue to be a valid + * json object. + * + * { + * "label": "label text", + * "uri": "http://example.com", + * "visited": true + * } + * + */ +static void linkbutton_apply_value(UiLinkButton *link, const char *jsonvalue) { + CxJson json; + cxJsonInit(&json, NULL); + cxJsonFill(&json, jsonvalue); + + CxJsonValue *value; + if(cxJsonNext(&json, &value) == CX_JSON_NO_ERROR) { + if(cxJsonIsObject(value)) { + CxJsonValue *label = cxJsonObjGet(value, "label"); + CxJsonValue *uri = cxJsonObjGet(value, "uri"); + CxJsonValue *visited = cxJsonObjGet(value, "visited"); + if(label) { + gtk_button_set_label(GTK_BUTTON(link->widget), cxJsonIsString(label) ? cxJsonAsString(label) : NULL); + } + if(uri) { + linkbutton_set_uri(link, cxJsonIsString(uri) ? cxJsonAsString(uri) : NULL); + + } + if(visited) { + linkbutton_set_visited(link, cxJsonIsBool(visited) ? cxJsonAsBool(visited) : FALSE); + } + } + cxJsonValueFree(value); + } + cxJsonDestroy(&json); +} + +static char* create_linkbutton_jsonvalue(const char *label, const char *uri, gboolean include_null, gboolean visited, gboolean set_visited) { + CxJsonValue *obj = cxJsonCreateObj(NULL); + if(label) { + cxJsonObjPutString(obj, CX_STR("label"), label); + } else if(include_null) { + cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL); + } + + if(uri) { + cxJsonObjPutString(obj, CX_STR("uri"), uri); + } else if(include_null) { + cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL); + } + + if(set_visited) { + cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE); + } + + CxJsonWriter writer = cxJsonWriterCompact(); + CxBuffer buf; + cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND); + cxJsonWrite(&buf, obj, (cx_write_func)cxBufferWrite, &writer); + cxJsonValueFree(obj); + cxBufferTerminate(&buf); + + return buf.space; +} + +static char* linkbutton_get_value(UiLinkButton *link) { + const char *label = gtk_button_get_label(GTK_BUTTON(link->widget)); + const char *uri = linkbutton_get_uri(link); + gboolean visited = linkbutton_get_visited(link); + return create_linkbutton_jsonvalue(label, uri, TRUE, visited, TRUE); +} + +static void linkbutton_callback(GtkWidget *widget, UiLinkButton *data) { + if(data->onclick) { + UiEvent e; + e.obj = data->obj; + e.document = e.obj->ctx->document; + e.window = e.obj->window; + e.eventdata = (char*)linkbutton_get_uri(data); + e.eventdatatype = UI_EVENT_DATA_STRING; + e.intval = 0; + e.set = ui_get_setop(); + data->onclick(&e, data->onclickdata); + } +} + +static void linkbutton_clicked(GtkWidget *widget, UiLinkButton *data) { + linkbutton_callback(widget, data); + if(data->link) { +#if GTK_CHECK_VERSION(4, 0, 0) + GtkUriLauncher *launcher = gtk_uri_launcher_new (data->link); + gtk_uri_launcher_launch (launcher, NULL, NULL, NULL, NULL); + g_object_unref (launcher); +#elif GTK_CHECK_VERSION(3, 22, 0) + GError *error = NULL; + gtk_show_uri_on_window(NULL, data->link, GDK_CURRENT_TIME, &error); +#elif + // TODO: call xdg-open +#endif + } +} + +static gboolean linkbutton_activate_link(GtkLinkButton *self, UiLinkButton *data) { + linkbutton_callback(data->widget, data); + return data->nofollow; +} + +UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) { + UiLinkButton *data = malloc(sizeof(UiLinkButton)); + memset(data, 0, sizeof(UiLinkButton)); + data->obj = obj; + data->type = args->type; + data->nofollow = args->nofollow; + data->onclick = args->onclick; + data->onclickdata = args->onclickdata; + + GtkWidget *button; + if(args->type == UI_LINK_BUTTON) { + button = gtk_button_new(); + g_signal_connect( + button, + "clicked", + G_CALLBACK(linkbutton_clicked), + data); + } else { + button = gtk_link_button_new("file:///"); + g_signal_connect( + button, + "activate-link", + G_CALLBACK(linkbutton_activate_link), + data); + } + gtk_button_set_label(GTK_BUTTON(button), args->label); +#if GTK_CHECK_VERSION(4, 0, 0) + gtk_button_set_can_shrink(GTK_BUTTON(button), TRUE); +#elif GTK_MAJOR_VERSION == 3 + GtkWidget *child = gtk_bin_get_child(GTK_BIN(button)); + gtk_label_set_ellipsize(GTK_LABEL(child), PANGO_ELLIPSIZE_END); +#endif + g_object_set_data(G_OBJECT(button), "ui_linkbutton", data); + g_signal_connect( + button, + "destroy", + G_CALLBACK(ui_destroy_linkbutton), + data); + + data->widget = button; + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); + if(var) { + UiString *str = var->value; + char *current_value = ui_get(str); + if(current_value) { + linkbutton_apply_value(data, current_value); + } + + str->obj = data; + str->get = ui_linkbutton_get; + str->set = ui_linkbutton_set; + } + + ui_set_name_and_style(button, args->name, args->style_class); + ui_set_widget_groups(obj->ctx, button, args->groups); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, button, &layout); + + return button; +} + +char* ui_linkbutton_get(UiString *s) { + UiLinkButton *link = s->obj; + if(s->value.free) { + s->value.free(s->value.ptr); + } + s->value.ptr = linkbutton_get_value(link); + s->value.free = free; + return s->value.ptr; +} + +void ui_linkbutton_set(UiString *s, const char *str) { + linkbutton_apply_value(s->obj, str); + if(s->value.free) { + s->value.free(s->value.ptr); + } +#if GTK_MAJOR_VERSION == 3 + UiLinkButton *data = s->obj; + GtkWidget *child = gtk_bin_get_child(GTK_BIN(data->widget)); + gtk_label_set_ellipsize(GTK_LABEL(child), PANGO_ELLIPSIZE_END); +#endif +} + + +void ui_linkbutton_value_set(UiString *str, const char *label, const char *uri) { + char *value = create_linkbutton_jsonvalue(label, uri, TRUE, FALSE, TRUE); + ui_set(str, value); + free(value); +} + +void ui_linkbutton_value_set_label(UiString *str, const char *label) { + char *value = create_linkbutton_jsonvalue(label, NULL, FALSE, FALSE, TRUE); + ui_set(str, value); + free(value); +} + +void ui_linkbutton_value_set_uri(UiString *str, const char *uri) { + char *value = create_linkbutton_jsonvalue(NULL, uri, FALSE, FALSE, TRUE); + ui_set(str, value); + free(value); +} + +void ui_linkbutton_value_set_visited(UiString *str, UiBool visited) { + char *value = create_linkbutton_jsonvalue(NULL, NULL, FALSE, visited, TRUE); + ui_set(str, value); + free(value); +} + + +void ui_linkbutton_set_label(UIWIDGET button, const char *label) { + gtk_button_set_label(GTK_BUTTON(button), label); +} + +void ui_linkbutton_set_uri(UIWIDGET button, const char *label) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + linkbutton_set_uri(link, label); + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } +} + +void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + linkbutton_set_visited(link, visited); + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } +} + +char* ui_linkbutton_get_label(UIWIDGET button) { + const char *label = gtk_button_get_label(GTK_BUTTON(button)); + return label ? strdup(label) : NULL; +} + +char* ui_linkbutton_get_uri(UIWIDGET button) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + const char *uri = linkbutton_get_uri(link); + return uri ? strdup(uri) : NULL; + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } + return NULL; +} + +UiBool ui_linkbutton_get_visited(UIWIDGET button) { + UiLinkButton *link = g_object_get_data(G_OBJECT(button), "ui_linkbutton"); + if(link) { + return linkbutton_get_visited(link); + } else { + fprintf(stderr, "Error: ui_linkbutton_set_label: widget is not a linkbutton\n"); + } + return FALSE; +} diff --git a/ui/gtk/button.h b/ui/gtk/button.h index c855b35..ce94b90 100644 --- a/ui/gtk/button.h +++ b/ui/gtk/button.h @@ -37,6 +37,16 @@ extern "C" { #endif +typedef struct UiLinkButton { + UiObject *obj; + GtkWidget *widget; + UiLinkType type; + UiBool nofollow; + char *link; + ui_callback onclick; + void *onclickdata; +} UiLinkButton; + void ui_button_set_icon_name(GtkWidget *button, const char *icon_name); typedef void (*ui_toggled_func)(void*, void*); @@ -45,6 +55,7 @@ GtkWidget* ui_create_button( UiObject *obj, const char *label, const char *icon, + const char *tooltip, ui_callback onclick, void *userdata, int event_value, @@ -55,6 +66,7 @@ void ui_setup_togglebutton( GtkWidget *togglebutton, const char *label, const char *icon, + const char *tooltip, const char *varname, UiInteger *value, ui_callback onchange, @@ -86,9 +98,15 @@ UIWIDGET ui_radiobutton_var(UiObject *obj, char *label, UiVar *var); void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event); +int64_t ui_switch_get(UiInteger *value); +void ui_switch_set(UiInteger *value, int64_t i); + int64_t ui_radiobutton_get(UiInteger *value); void ui_radiobutton_set(UiInteger *value, int64_t i); +char* ui_linkbutton_get(UiString *s); +void ui_linkbutton_set(UiString *s, const char *str); + #ifdef __cplusplus } #endif diff --git a/ui/gtk/container.c b/ui/gtk/container.c index 7a98bf4..9ca07f9 100644 --- a/ui/gtk/container.c +++ b/ui/gtk/container.c @@ -28,6 +28,7 @@ #include #include +#include #include #include "container.h" @@ -36,17 +37,20 @@ #include "../common/context.h" #include "../common/object.h" +#include "../common/container.h" + +#include "../ui/properties.h" void ui_container_begin_close(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); + UiContainerX *ct = obj->container_end; ct->close = 1; } int ui_container_finish(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); + UiContainerX *ct = obj->container_end; if(ct->close) { - ui_end(obj); + ui_end_new(obj); return 0; } return 1; @@ -70,7 +74,7 @@ GtkWidget* ui_gtk_hbox_new(int spacing) { GtkWidget* ui_subcontainer_create( UiSubContainerType type, - UiObject *newobj, + UiObject *obj, int spacing, int columnspacing, int rowspacing, @@ -78,38 +82,39 @@ GtkWidget* ui_subcontainer_create( { GtkWidget *sub = NULL; GtkWidget *add = NULL; + UiContainerX *container = NULL; switch(type) { default: { sub = ui_gtk_vbox_new(spacing); - add = ui_box_set_margin(sub, margin); - newobj->container = ui_box_container(newobj, sub, type); - newobj->widget = sub; + add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0); + container = ui_box_container(obj, sub, type); break; } case UI_CONTAINER_HBOX: { sub = ui_gtk_hbox_new(spacing); - add = ui_box_set_margin(sub, margin); - newobj->container = ui_box_container(newobj, sub, type); - newobj->widget = sub; + add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0); + container = ui_box_container(obj, sub, type); break; } case UI_CONTAINER_GRID: { sub = ui_create_grid_widget(columnspacing, rowspacing); - add = ui_box_set_margin(sub, margin); - newobj->container = ui_grid_container(newobj, sub, FALSE, FALSE, FALSE, FALSE); - newobj->widget = sub; + add = ui_gtk_set_margin(sub, margin, 0, 0, 0, 0); + container = ui_grid_container(obj, sub, FALSE, FALSE, FALSE, FALSE); break; } case UI_CONTAINER_NO_SUB: { break; } } + if(container) { + uic_object_push_container(obj, container); + } return add; } /* -------------------- Box Container -------------------- */ -UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) { +UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) { UiBoxContainer *ct = cxCalloc( obj->ctx->allocator, 1, @@ -117,12 +122,14 @@ UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType ct->container.widget = box; ct->container.add = ui_box_container_add; ct->type = type; - return (UiContainer*)ct; + return (UiContainerX*)ct; } -void ui_box_container_add(UiContainer *ct, GtkWidget *widget) { +void ui_box_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { UiBoxContainer *bc = (UiBoxContainer*)ct; - UiBool fill = ct->layout.fill; + widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom); + + UiBool fill = layout->fill; if(bc->has_fill && fill) { fprintf(stderr, "UiError: container has 2 filled widgets"); fill = FALSE; @@ -149,11 +156,10 @@ void ui_box_container_add(UiContainer *ct, GtkWidget *widget) { gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0); #endif - ui_reset_layout(ct->layout); ct->current = widget; } -UiContainer* ui_grid_container( +UiContainerX* ui_grid_container( UiObject *obj, GtkWidget *grid, UiBool def_hexpand, @@ -173,140 +179,72 @@ UiContainer* ui_grid_container( ct->container.add = ui_grid_container_add; UI_GTK_V2(ct->width = 0); UI_GTK_V2(ct->height = 1); - return (UiContainer*)ct; + return (UiContainerX*)ct; } + #if GTK_MAJOR_VERSION >= 3 -void ui_grid_container_add(UiContainer *ct, GtkWidget *widget) { +void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { UiGridContainer *grid = (UiGridContainer*)ct; + widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom); - if(ct->layout.newline) { + if(ct->container.newline) { grid->x = 0; grid->y++; - ct->layout.newline = FALSE; + ct->container.newline = FALSE; } - int hexpand = FALSE; - int vexpand = FALSE; - int hfill = FALSE; - int vfill = FALSE; - if(!ct->layout.override_defaults) { - if(grid->def_hexpand) { - hexpand = TRUE; - hfill = TRUE; - } else if(grid->def_hfill) { - hfill = TRUE; - } - if(grid->def_vexpand) { - vexpand = TRUE; - vfill = TRUE; - } else if(grid->def_vfill) { - vfill = TRUE; - } - } - - UiBool fill = ct->layout.fill; - if(ct->layout.hexpand) { - hexpand = TRUE; - hfill = TRUE; - } else if(ct->layout.hfill) { - hfill = TRUE; - } - if(ct->layout.vexpand) { - vexpand = TRUE; - vfill = TRUE; - } else if(ct->layout.vfill) { - vfill = TRUE; - } - if(fill) { - hfill = TRUE; - vfill = TRUE; - } + uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill); - if(!hfill) { + if(!layout->hfill) { gtk_widget_set_halign(widget, GTK_ALIGN_START); } - if(!vfill) { + if(!layout->vfill) { gtk_widget_set_valign(widget, GTK_ALIGN_START); } - gtk_widget_set_hexpand(widget, hexpand); - gtk_widget_set_vexpand(widget, vexpand); + gtk_widget_set_hexpand(widget, layout->hexpand); + gtk_widget_set_vexpand(widget, layout->vexpand); - int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1; - int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1; + int colspan = layout->colspan > 0 ? layout->colspan : 1; + int rowspan = layout->rowspan > 0 ? layout->rowspan : 1; gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan); grid->x += colspan; - ui_reset_layout(ct->layout); - ct->current = widget; + grid->container.current = widget; } #endif #ifdef UI_GTK2 -void ui_grid_container_add(UiContainer *ct, GtkWidget *widget) { +void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget) { UiGridContainer *grid = (UiGridContainer*)ct; + widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom); - if(ct->layout.newline) { + if(ct->container.newline) { grid->x = 0; grid->y++; - ct->layout.newline = FALSE; + ct->container.newline = FALSE; } - int hexpand = FALSE; - int vexpand = FALSE; - int hfill = FALSE; - int vfill = FALSE; - if(!ct->layout.override_defaults) { - if(grid->def_hexpand) { - hexpand = TRUE; - hfill = TRUE; - } else if(grid->def_hfill) { - hfill = TRUE; - } - if(grid->def_vexpand) { - vexpand = TRUE; - vfill = TRUE; - } else if(grid->def_vfill) { - vfill = TRUE; - } - } - - UiBool fill = ct->layout.fill; - if(ct->layout.hexpand) { - hexpand = TRUE; - hfill = TRUE; - } else if(ct->layout.hfill) { - hfill = TRUE; - } - if(ct->layout.vexpand) { - vexpand = TRUE; - vfill = TRUE; - } else if(ct->layout.vfill) { - vfill = TRUE; - } - if(fill) { - hfill = TRUE; - vfill = TRUE; - } + uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill); GtkAttachOptions xoptions = 0; GtkAttachOptions yoptions = 0; - if(hexpand) { + if(layout->hexpand) { xoptions = GTK_EXPAND; } - if(hfill) { + if(layout->hfill) { xoptions |= GTK_FILL; } - if(vexpand) { + if(layout->vexpand) { yoptions = GTK_EXPAND; } - if(vfill) { + if(layout->vfill) { yoptions |= GTK_FILL; } - int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1; - int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1; + int colspan = layout->colspan > 0 ? layout->colspan : 1; + int rowspan = layout->rowspan > 0 ? layout->rowspan : 1; // TODO: use colspan/rowspan gtk_table_attach(GTK_TABLE(ct->widget), widget, grid->x, grid->x+1, grid->y, grid->y+1, xoptions, yoptions, 0, 0); @@ -318,116 +256,138 @@ void ui_grid_container_add(UiContainer *ct, GtkWidget *widget) { gtk_table_resize(GTK_TABLE(ct->widget), grid->width, grid->height); } - ui_reset_layout(ct->layout); ct->current = widget; } #endif -UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) { - UiContainer *ct = cxCalloc( +UiContainerX* ui_frame_container(UiObject *obj, GtkWidget *frame) { + UiContainerPrivate *ct = cxCalloc( obj->ctx->allocator, 1, - sizeof(UiContainer)); + sizeof(UiContainerPrivate)); ct->widget = frame; ct->add = ui_frame_container_add; - return ct; + return (UiContainerX*)ct; } -void ui_frame_container_add(UiContainer *ct, GtkWidget *widget) { +void ui_frame_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { + widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom); FRAME_SET_CHILD(ct->widget, widget); + ct->current = widget; } -UiContainer* ui_expander_container(UiObject *obj, GtkWidget *expander) { - UiContainer *ct = cxCalloc( +UiContainerX* ui_expander_container(UiObject *obj, GtkWidget *expander) { + UiContainerPrivate *ct = cxCalloc( obj->ctx->allocator, 1, - sizeof(UiContainer)); + sizeof(UiContainerPrivate)); ct->widget = expander; ct->add = ui_expander_container_add; - return ct; + return (UiContainerX*)ct; } -void ui_expander_container_add(UiContainer *ct, GtkWidget *widget) { +void ui_expander_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { + widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom); EXPANDER_SET_CHILD(ct->widget, widget); + ct->current = widget; } -void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget) { +void ui_scrolledwindow_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { + widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom); // TODO: check if the widget implements GtkScrollable SCROLLEDWINDOW_SET_CHILD(ct->widget, widget); - ui_reset_layout(ct->layout); ct->current = widget; } -UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) { - UiContainer *ct = cxCalloc( +UiContainerX* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) { + UiContainerPrivate *ct = cxCalloc( obj->ctx->allocator, 1, - sizeof(UiContainer)); + sizeof(UiContainerPrivate)); ct->widget = scrolledwindow; ct->add = ui_scrolledwindow_container_add; - return ct; + return (UiContainerX*)ct; } -UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview) { +UiContainerX* ui_tabview_container(UiObject *obj, GtkWidget *tabview) { UiTabViewContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiTabViewContainer)); ct->container.widget = tabview; ct->container.add = ui_tabview_container_add; - return (UiContainer*)ct; + return (UiContainerX*)ct; } -void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget) { +void ui_tabview_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { UiGtkTabView *data = ui_widget_get_tabview_data(ct->widget); if(!data) { fprintf(stderr, "UI Error: widget is not a tabview"); return; } - data->add_tab(ct->widget, -1, ct->layout.label, widget); + widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom); + data->add_tab(ct->widget, -1, layout->label, widget); - ui_reset_layout(ct->layout); ct->current = widget; } +#ifdef UI_GTK2 +static void alignment_child_visibility_changed(GtkWidget *widget, gpointer user_data) { + gtk_widget_set_visible(gtk_widget_get_parent(widget), gtk_widget_get_visible(widget)); +} -GtkWidget* ui_box_set_margin(GtkWidget *box, int margin) { - GtkWidget *ret = box; +#endif + +GtkWidget* ui_gtk_set_margin(GtkWidget *widget, int margin, int margin_left, int margin_right, int margin_top, int margin_bottom) { + if(margin > 0) { + margin_left = margin; + margin_right = margin; + margin_top = margin; + margin_bottom = margin; + } + GtkWidget *ret = widget; #if GTK_MAJOR_VERSION >= 3 -#if GTK_MAJOR_VERSION * 1000 + GTK_MINOR_VERSION >= 3012 - gtk_widget_set_margin_start(box, margin); - gtk_widget_set_margin_end(box, margin); +#if GTK_CHECK_VERSION(3, 12, 0) + gtk_widget_set_margin_start(widget, margin_left); + gtk_widget_set_margin_end(widget, margin_right); #else - gtk_widget_set_margin_left(box, margin); - gtk_widget_set_margin_right(box, margin); + gtk_widget_set_margin_left(widget, margin_left); + gtk_widget_set_margin_right(widget, margin_right); #endif - gtk_widget_set_margin_top(box, margin); - gtk_widget_set_margin_bottom(box, margin); + gtk_widget_set_margin_top(widget, margin_top); + gtk_widget_set_margin_bottom(widget, margin_bottom); #elif defined(UI_GTK2) GtkWidget *a = gtk_alignment_new(0.5, 0.5, 1, 1); - gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin, margin, margin, margin); - gtk_container_add(GTK_CONTAINER(a), box); + gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin_top, margin_bottom, margin_left, margin_right); + gtk_container_add(GTK_CONTAINER(a), widget); + g_signal_connect( + widget, + "show", + G_CALLBACK(alignment_child_visibility_changed), + NULL); + g_signal_connect( + widget, + "hide", + G_CALLBACK(alignment_child_visibility_changed), + NULL); ret = a; #endif return ret; } UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, UiSubContainerType type) { - UiObject *current = uic_current_obj(obj); - UiContainer *ct = current->container; - UI_APPLY_LAYOUT2(current, args); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); GtkWidget *box = type == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args->spacing) : ui_gtk_hbox_new(args->spacing); ui_set_name_and_style(box, args->name, args->style_class); - GtkWidget *widget = args->margin > 0 ? ui_box_set_margin(box, args->margin) : box; - ct->add(ct, widget); + ct->add(ct, box, &layout); - UiObject *newobj = uic_object_new(obj, box); - newobj->container = ui_box_container(obj, box, type); - uic_obj_add(obj, newobj); + UiContainerX *container = ui_box_container(obj, box, type); + uic_object_push_container(obj, container); - return widget; + return box; } UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) { @@ -452,82 +412,113 @@ GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing) { } UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { - UiObject* current = uic_current_obj(obj); - UI_APPLY_LAYOUT2(current, args); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); GtkWidget *widget; GtkWidget *grid = ui_create_grid_widget(args->columnspacing, args->rowspacing); ui_set_name_and_style(grid, args->name, args->style_class); - widget = ui_box_set_margin(grid, args->margin); - current->container->add(current->container, widget); + ct->add(ct, grid, &layout); - UiObject *newobj = uic_object_new(obj, grid); - newobj->container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill); - uic_obj_add(obj, newobj); + UiContainerX *container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill); + uic_object_push_container(obj, container); - return widget; + return grid; +} + +static void frame_create_subcontainer(UiObject *obj, UiFrameArgs *args) { + switch(args->subcontainer) { + default: + case UI_CONTAINER_VBOX: { + UiContainerArgs sub_args = { .spacing = args->spacing, .margin = args->padding }; + ui_vbox_create(obj, &sub_args); + break; + } + case UI_CONTAINER_HBOX: { + UiContainerArgs sub_args = { .spacing = args->spacing, .margin = args->padding }; + ui_hbox_create(obj, &sub_args); + break; + } + case UI_CONTAINER_GRID: { + UiContainerArgs sub_args = { .columnspacing = args->columnspacing, .rowspacing = args->rowspacing, .margin = args->padding }; + ui_grid_create(obj, &sub_args); + break; + } + case UI_CONTAINER_NO_SUB: { + break; // NOOP + } + } } UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) { - UiObject* current = uic_current_obj(obj); - UI_APPLY_LAYOUT2(current, args); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); GtkWidget *frame = gtk_frame_new(args->label); - UiObject *newobj = uic_object_new(obj, frame); - GtkWidget *sub = ui_subcontainer_create(args->subcontainer, newobj, args->spacing, args->columnspacing, args->rowspacing, args->margin); + ct->add(ct, frame, &layout); + + GtkWidget *sub = ui_subcontainer_create( + args->subcontainer, + obj, args->spacing, + args->columnspacing, + args->rowspacing, + args->padding); if(sub) { FRAME_SET_CHILD(frame, sub); } else { - newobj->widget = frame; - newobj->container = ui_frame_container(obj, frame); + UiContainerX *container = ui_frame_container(obj, frame); + uic_object_push_container(obj, container); } - current->container->add(current->container, frame); - uic_obj_add(obj, newobj); return frame; } UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs *args) { - UiObject* current = uic_current_obj(obj); - UI_APPLY_LAYOUT2(current, args); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); GtkWidget *expander = gtk_expander_new(args->label); gtk_expander_set_expanded(GTK_EXPANDER(expander), args->isexpanded); - UiObject *newobj = uic_object_new(obj, expander); - GtkWidget *sub = ui_subcontainer_create(args->subcontainer, newobj, args->spacing, args->columnspacing, args->rowspacing, args->margin); + ct->add(ct, expander, &layout); + + GtkWidget *sub = ui_subcontainer_create( + args->subcontainer, + obj, args->spacing, + args->columnspacing, + args->rowspacing, + args->padding); if(sub) { EXPANDER_SET_CHILD(expander, sub); } else { - newobj->widget = expander; - newobj->container = ui_expander_container(obj, expander); + UiContainerX *container = ui_expander_container(obj, expander); + uic_object_push_container(obj, container); } - current->container->add(current->container, expander); - uic_obj_add(obj, newobj); return expander; } UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs *args) { - UiObject* current = uic_current_obj(obj); - UI_APPLY_LAYOUT2(current, args); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); GtkWidget *sw = SCROLLEDWINDOW_NEW(); ui_set_name_and_style(sw, args->name, args->style_class); - GtkWidget *widget = ui_box_set_margin(sw, args->margin); - current->container->add(current->container, widget); - - UiObject *newobj = uic_object_new(obj, sw); - GtkWidget *sub = ui_subcontainer_create(args->subcontainer, newobj, args->spacing, args->columnspacing, args->rowspacing, args->margin); + ct->add(ct, sw, &layout); + + GtkWidget *sub = ui_subcontainer_create( + args->subcontainer, + obj, args->spacing, + args->columnspacing, + args->rowspacing, + args->padding); if(sub) { SCROLLEDWINDOW_SET_CHILD(sw, sub); } else { - newobj->widget = sw; - newobj->container = ui_scrolledwindow_container(obj, sw); + UiContainerX *container = ui_scrolledwindow_container(obj, sw); + uic_object_push_container(obj, container); } - uic_obj_add(obj, newobj); - return sw; } @@ -736,7 +727,7 @@ typedef void (*ui_tabview_set_func)(UiInteger*, int64_t); UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs *args) { UiGtkTabView *data = malloc(sizeof(UiGtkTabView)); memset(data, 0, sizeof(UiGtkTabView)); - data->margin = args->margin; + data->padding = args->padding; data->spacing = args->spacing; data->columnspacing = args->columnspacing; data->rowspacing = args->rowspacing; @@ -796,9 +787,8 @@ UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs *args) { } } - UiObject* current = uic_current_obj(obj); if(args->value || args->varname) { - UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_INTEGER); + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); UiInteger *i = var->value; i->get = getfunc; i->set = setfunc; @@ -807,29 +797,56 @@ UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs *args) { g_object_set_data(G_OBJECT(widget), "ui_tabview", data); data->widget = data_widget; - data->subcontainer = args->subcontainer; - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, widget); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); - UiObject *newobj = uic_object_new(obj, widget); - newobj->container = ui_tabview_container(obj, widget); - uic_obj_add(obj, newobj); - data->obj = newobj; + UiContainerX *container = ui_tabview_container(obj, widget); + uic_object_push_container(obj, container); return widget; } +static GtkWidget* create_tab(UiObject *obj, UiGtkTabView *tabview, const char *title, int tab) { + UiContainerX *container; + GtkWidget *sub; + switch(tabview->subcontainer) { + default: { + sub = ui_gtk_vbox_new(tabview->spacing); + container = ui_box_container(obj, sub, tabview->subcontainer); + break; + } + case UI_CONTAINER_HBOX: { + sub = ui_gtk_hbox_new(tabview->spacing); + container = ui_box_container(obj, sub, tabview->subcontainer); + break; + } + case UI_CONTAINER_GRID: { + sub = ui_create_grid_widget(tabview->columnspacing, tabview->rowspacing); + container = ui_grid_container(obj, sub, FALSE, FALSE, FALSE, FALSE); + break; + } + } + + uic_object_push_container(obj, container); + + GtkWidget *widget = ui_gtk_set_margin(sub, tabview->padding, 0, 0, 0, 0); + tabview->add_tab(tabview->widget, tab, title, widget); + + return sub; +} + void ui_tab_create(UiObject* obj, const char* title) { - UiObject* current = uic_current_obj(obj); - UiGtkTabView *data = ui_widget_get_tabview_data(current->widget); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + GtkWidget *tabview = ct->widget; + UiGtkTabView *data = ui_widget_get_tabview_data(tabview); if(!data) { fprintf(stderr, "UI Error: widget is not a tabview\n"); return; } - UiObject *newobj = ui_tabview_add(current->widget, title, -1); - current->next = newobj; + create_tab(obj, data, title, -1); } @@ -859,31 +876,8 @@ UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) { return NULL; } - UiObject *newobj = cxCalloc(data->obj->ctx->allocator, 1, sizeof(UiObject)); - newobj->ctx = data->obj->ctx; - - GtkWidget *sub; - switch(data->subcontainer) { - default: { - sub = ui_gtk_vbox_new(data->spacing); - newobj->container = ui_box_container(newobj, sub, data->subcontainer); - break; - } - case UI_CONTAINER_HBOX: { - sub = ui_gtk_hbox_new(data->spacing); - newobj->container = ui_box_container(newobj, sub, data->subcontainer); - break; - } - case UI_CONTAINER_GRID: { - sub = ui_create_grid_widget(data->columnspacing, data->rowspacing); - newobj->container = ui_grid_container(newobj, sub, FALSE, FALSE, FALSE, FALSE); - break; - } - } - newobj->widget = sub; - GtkWidget *widget = ui_box_set_margin(sub, data->margin); - - data->add_tab(data->widget, tab_index, name, widget); + UiObject *newobj = uic_object_new_toplevel(); + newobj->widget = create_tab(newobj, data, name, tab_index); return newobj; } @@ -892,20 +886,16 @@ UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) { /* -------------------- Headerbar -------------------- */ static void hb_set_part(UiObject *obj, int part) { - UiObject* current = uic_current_obj(obj); - GtkWidget *headerbar = current->widget; + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + GtkWidget *headerbar = ct->widget; UiHeaderbarContainer *hb = cxCalloc( obj->ctx->allocator, 1, sizeof(UiHeaderbarContainer)); - memcpy(hb, current->container, sizeof(UiHeaderbarContainer)); - - UiObject *newobj = uic_object_new(obj, headerbar); - newobj->container = (UiContainer*)hb; - uic_obj_add(obj, newobj); - + memcpy(hb, ct, sizeof(UiHeaderbarContainer)); hb->part = part; + uic_object_push_container(obj, (UiContainerX*)hb); } void ui_headerbar_start_create(UiObject *obj) { @@ -921,74 +911,70 @@ void ui_headerbar_end_create(UiObject *obj) { } UIWIDGET ui_headerbar_fallback_create(UiObject *obj, UiHeaderbarArgs *args) { - UiObject *current = uic_current_obj(obj); - UiContainer *ct = current->container; - UI_APPLY_LAYOUT2(current, args); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); GtkWidget *box = ui_gtk_hbox_new(args->alt_spacing); ui_set_name_and_style(box, args->name, args->style_class); - ct->add(ct, box); + ct->add(ct, box, &layout); - UiObject *newobj = uic_object_new(obj, box); - newobj->container = ui_headerbar_fallback_container(obj, box); - uic_obj_add(obj, newobj); + UiContainerX *container = ui_headerbar_fallback_container(obj, box); + uic_object_push_container(obj, container); return box; } static void hb_fallback_set_part(UiObject *obj, int part) { - UiObject* current = uic_current_obj(obj); - GtkWidget *headerbar = current->widget; + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + GtkWidget *headerbar = ct->widget; - UiObject *newobj = uic_object_new(obj, headerbar); - newobj->container = ui_headerbar_container(obj, headerbar); - uic_obj_add(obj, newobj); + UiContainerX *container = ui_headerbar_container(obj, headerbar); + uic_object_push_container(obj, container); - UiHeaderbarContainer *hb = (UiHeaderbarContainer*)newobj->container; + UiHeaderbarContainer *hb = (UiHeaderbarContainer*)container; hb->part = part; } -UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) { +UiContainerX* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) { UiHeaderbarContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiHeaderbarContainer)); ct->container.widget = headerbar; ct->container.add = ui_headerbar_fallback_container_add; - return (UiContainer*)ct; + return (UiContainerX*)ct; } -void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget) { +void ui_headerbar_fallback_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct; BOX_ADD(ct->widget, widget); } #if GTK_CHECK_VERSION(3, 10, 0) -UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args) { +UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args) { GtkWidget *headerbar = g_object_get_data(G_OBJECT(obj->widget), "ui_headerbar"); if(!headerbar) { return ui_headerbar_fallback_create(obj, args); } - UiObject *newobj = uic_object_new(obj, headerbar); - newobj->container = ui_headerbar_container(obj, headerbar); - uic_obj_add(obj, newobj); + UiContainerX *container = ui_headerbar_container(obj, headerbar); + uic_object_push_container(obj, container); return headerbar; } -UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) { +UiContainerX* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) { UiHeaderbarContainer *ct = cxCalloc( obj->ctx->allocator, 1, sizeof(UiHeaderbarContainer)); ct->container.widget = headerbar; ct->container.add = ui_headerbar_container_add; - return (UiContainer*)ct; + return (UiContainerX*)ct; } -void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget) { +void ui_headerbar_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct; if(hb->part == 0) { UI_HEADERBAR_PACK_START(ct->widget, widget); @@ -1023,12 +1009,11 @@ UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) { } GtkWidget *box = ui_gtk_vbox_new(args->spacing); - ui_box_set_margin(box, args->margin); + ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom); adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), box); - UiObject *newobj = uic_object_new(obj, box); - newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX); - uic_obj_add(obj, newobj); + UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX); + uic_object_push_container(obj, container); return box; } @@ -1037,17 +1022,44 @@ UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) { GtkWidget *sidebar_vbox = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar"); GtkWidget *box = ui_gtk_vbox_new(args->spacing); - ui_box_set_margin(box, args->margin); + ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom); BOX_ADD_EXPAND(sidebar_vbox, box); - UiObject *newobj = uic_object_new(obj, box); - newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX); - uic_obj_add(obj, newobj); + UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX); + uic_object_push_container(obj, container); return box; } #endif +/* ------------------------ Split Window Panels ------------------------ */ + +static UIWIDGET splitwindow_panel(UiObject *obj, GtkWidget *pbox, UiSidebarArgs *args) { + if(!pbox) { + fprintf(stderr, "Error: window is not a splitview window\n"); + return NULL; + } + + GtkWidget *box = ui_gtk_vbox_new(args->spacing); + ui_set_name_and_style(box, args->name, args->style_class); + ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom); + BOX_ADD_EXPAND(pbox, box); + + UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX); + uic_object_push_container(obj, container); + + return box; +} + +UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args) { + return splitwindow_panel(obj, g_object_get_data(G_OBJECT(obj->widget), "ui_left_panel"), args); +} + +UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args) { + return splitwindow_panel(obj, g_object_get_data(G_OBJECT(obj->widget), "ui_right_panel"), args); +} + + /* -------------------- Splitpane -------------------- */ static GtkWidget* create_paned(UiOrientation orientation) { @@ -1065,23 +1077,55 @@ static GtkWidget* create_paned(UiOrientation orientation) { return NULL; } - +static void save_pane_pos(GtkWidget *widget, char *property_name) { + int pos = gtk_paned_get_position(GTK_PANED(widget)); + char buf[32]; + snprintf(buf, 32, "%d", pos); + ui_set_property(property_name, buf); + free(property_name); +} static UIWIDGET splitpane_create(UiObject *obj, UiOrientation orientation, UiSplitPaneArgs *args) { - UiObject* current = uic_current_obj(obj); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); GtkWidget *pane0 = create_paned(orientation); - - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, pane0); + ct->add(ct, pane0, &layout); int max = args->max_panes == 0 ? 2 : args->max_panes; - UiObject *newobj = uic_object_new(obj, pane0); - newobj->container = ui_splitpane_container(obj, pane0, orientation, max, args->initial_position); - uic_obj_add(obj, newobj); + if(args->position_property) { + const char *pos_str = ui_get_property(args->position_property); + if(pos_str) { + char *end; + long pos = strtol(pos_str, &end, 10); + if(*end == '\0') { + args->initial_position = (int)pos; + } + } + + g_signal_connect( + pane0, + "destroy", + G_CALLBACK(save_pane_pos), + strdup(args->position_property)); + } + + UiSplitPane *splitpane = ui_create_splitpane_data(pane0, orientation, max, args->initial_position); + UiContainerX *container = ui_splitpane_container(obj, pane0, splitpane); + uic_object_push_container(obj, container); - g_object_set_data(G_OBJECT(pane0), "ui_splitpane", newobj->container); + g_object_set_data(G_OBJECT(pane0), "ui_splitpane", splitpane); + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); + if(var) { + UiInteger *i = var->value; + splitpane->initial_position = i->value; + + i->obj = splitpane; + i->get = ui_splitpane_get; + i->set = ui_splitpane_set; + } return pane0; } @@ -1094,20 +1138,27 @@ UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) { return splitpane_create(obj, UI_VERTICAL, args); } -UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max, int init) { - UiSplitPaneContainer *ct = ui_calloc(obj->ctx, 1, sizeof(UiSplitPaneContainer)); - ct->container.widget = pane; - ct->container.add = ui_splitpane_container_add; +UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init) { + UiSplitPane *ct = malloc(sizeof(UiSplitPane)); ct->current_pane = pane; ct->orientation = orientation; ct->max = max; ct->initial_position = init; ct->children = cxArrayListCreateSimple(CX_STORE_POINTERS, 4); - return (UiContainer*)ct; + return ct; +} + +UiContainerX* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiSplitPane *data) { + UiSplitPaneContainer *ct = ui_calloc(obj->ctx, 1, sizeof(UiSplitPaneContainer)); + ct->container.widget = pane; + ct->container.add = ui_splitpane_container_add; + ct->splitpane = data; + return (UiContainerX*)ct; } -void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget) { - UiSplitPaneContainer *s = (UiSplitPaneContainer*)ct; +void ui_splitpane_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) { + UiSplitPaneContainer *sct = (UiSplitPaneContainer*)ct; + UiSplitPane *s = sct->splitpane; if(s->nchildren >= s->max) { fprintf(stderr, "splitpane: maximum number of children reached\n"); @@ -1138,14 +1189,26 @@ void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget) { } } +int64_t ui_splitpane_get(UiInteger *i) { + UiSplitPane *s = i->obj; + i->value = gtk_paned_get_position(GTK_PANED(s->current_pane)); + return i->value; +} + +void ui_splitpane_set(UiInteger *i, int64_t value) { + UiSplitPane *s = i->obj; + i->value = value; + gtk_paned_set_position(GTK_PANED(s->current_pane), (int)value); +} + UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBool visible) { - UiSplitPaneContainer *ct = g_object_get_data(G_OBJECT(splitpane), "ui_splitpane"); - if(!ct) { + UiSplitPane *s = g_object_get_data(G_OBJECT(splitpane), "ui_splitpane"); + if(!s) { fprintf(stderr, "UI Error: not a splitpane\n"); return; } - GtkWidget *w = cxListAt(ct->children, child_index); + GtkWidget *w = cxListAt(s->children, child_index); if(w) { gtk_widget_set_visible(w, visible); } @@ -1199,13 +1262,12 @@ static void update_itemlist(UiList *list, int c) { UiObject *item_obj = cxMapGet(ct->current_items, key); if(item_obj) { // re-add previously created widget - ui_box_container_add(ct->container, item_obj->widget); + UiLayout layout = {0}; + ui_box_container_add(ct->container, item_obj->widget, &layout); } else { // create new widget and object for this list element - CxMempool *mp = cxMempoolCreateSimple(256); - const CxAllocator *a = mp->allocator; - UiObject *obj = cxCalloc(a, 1, sizeof(UiObject)); - obj->ctx = uic_context(obj, mp); + UiObject *obj = uic_object_new_toplevel(); + obj->ctx->parent = ct->parent->ctx; obj->window = NULL; obj->widget = ui_subcontainer_create( ct->subcontainer, @@ -1214,7 +1276,8 @@ static void update_itemlist(UiList *list, int c) { ct->columnspacing, ct->rowspacing, ct->margin); - ui_box_container_add(ct->container, obj->widget); + UiLayout layout = {0}; + ui_box_container_add(ct->container, obj->widget, &layout); if(ct->create_ui) { ct->create_ui(obj, index, elm, ct->userdata); } @@ -1236,19 +1299,17 @@ static void destroy_itemlist_container(GtkWidget *w, UiGtkItemListContainer *con } UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) { - UiObject *current = uic_current_obj(obj); - UiContainer *ct = current->container; - UI_APPLY_LAYOUT2(current, args); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); GtkWidget *box = args->container == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args->spacing) : ui_gtk_hbox_new(args->spacing); ui_set_name_and_style(box, args->name, args->style_class); - GtkWidget *widget = args->margin > 0 ? ui_box_set_margin(box, args->margin) : box; - ct->add(ct, widget); + ct->add(ct, box, &layout); UiGtkItemListContainer *container = malloc(sizeof(UiGtkItemListContainer)); container->parent = obj; container->widget = box; - container->container = ui_box_container(current, box, args->container); + container->container = (UiContainerPrivate*)ui_box_container(obj, box, args->container); container->create_ui = args->create_ui; container->userdata = args->userdata; container->subcontainer = args->subcontainer; @@ -1261,7 +1322,7 @@ UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) { container->rowspacing = args->sub_rowspacing; container->remove_items = TRUE; - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_LIST); if(var) { UiList *list = var->value; list->obj = container; @@ -1278,56 +1339,3 @@ UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) { } - -/* - * -------------------- Layout Functions -------------------- - * - * functions for setting layout attributes for the current container - * - */ - -void ui_layout_fill(UiObject *obj, UiBool fill) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.fill = fill; -} - -void ui_layout_hexpand(UiObject *obj, UiBool expand) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.hexpand = expand; -} - -void ui_layout_vexpand(UiObject *obj, UiBool expand) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.vexpand = expand; -} - -void ui_layout_hfill(UiObject *obj, UiBool fill) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.hfill = fill; -} - -void ui_layout_vfill(UiObject *obj, UiBool fill) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.vfill = fill; -} - -UIEXPORT void ui_layout_override_defaults(UiObject *obj, UiBool d) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.override_defaults = d; -} - -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 --git a/ui/gtk/container.h b/ui/gtk/container.h index 5f44766..2f2510c 100644 --- a/ui/gtk/container.h +++ b/ui/gtk/container.h @@ -45,45 +45,30 @@ extern "C" { #define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout)) -typedef void (*ui_container_add_f)(UiContainer*, GtkWidget*); typedef struct UiDocumentView UiDocumentView; - -typedef struct UiLayout UiLayout; -struct UiLayout { - UiBool fill; - UiBool newline; - char *label; - UiBool hexpand; - UiBool vexpand; - UiBool hfill; - UiBool vfill; - UiBool override_defaults; - int width; - int colspan; - int rowspan; -}; - -struct UiContainer { +typedef struct UiContainerPrivate UiContainerPrivate; +struct UiContainerPrivate { + UiContainerX container; GtkWidget *widget; UIMENU menu; - GtkWidget *current; + GtkWidget *current; // TODO: remove - void (*add)(UiContainer*, GtkWidget*); + void (*add)(UiContainerPrivate*, GtkWidget*, UiLayout *layout); UiLayout layout; int close; }; typedef struct UiBoxContainer { - UiContainer container; + UiContainerPrivate container; UiSubContainerType type; UiBool has_fill; } UiBoxContainer; typedef struct UiGridContainer { - UiContainer container; + UiContainerPrivate container; UiBool def_hexpand; UiBool def_vexpand; UiBool def_hfill; @@ -97,7 +82,7 @@ typedef struct UiGridContainer { } UiGridContainer; typedef struct UiTabViewContainer { - UiContainer container; + UiContainerPrivate container; } UiTabViewContainer; typedef void (*ui_select_tab_func)(UIWIDGET widget, int tab); @@ -110,7 +95,7 @@ typedef struct UiGtkTabView { ui_select_tab_func remove_tab; ui_add_tab_func add_tab; UiSubContainerType subcontainer; - int margin; + int padding; int spacing; int columnspacing; int rowspacing; @@ -118,9 +103,7 @@ typedef struct UiGtkTabView { void *onchangedata; } UiGtkTabView; - -typedef struct UiSplitPaneContainer { - UiContainer container; +typedef struct UiSplitPane { GtkWidget *current_pane; CxList *children; UiOrientation orientation; @@ -128,10 +111,15 @@ typedef struct UiSplitPaneContainer { int max; int nchildren; int initial_position; +} UiSplitPane; + +typedef struct UiSplitPaneContainer { + UiContainerPrivate container; + UiSplitPane *splitpane; } UiSplitPaneContainer; typedef struct UiHeaderbarContainer { - UiContainer container; + UiContainerPrivate container; GtkWidget *centerbox; int part; UiHeaderbarAlternative alternative; /* only used by fallback headerbar */ @@ -140,7 +128,7 @@ typedef struct UiHeaderbarContainer { typedef struct UiGtkItemListContainer { UiObject *parent; GtkWidget *widget; - UiContainer *container; + UiContainerPrivate *container; void (*create_ui)(UiObject *, int, void *, void *); void *userdata; UiSubContainerType subcontainer; @@ -163,52 +151,52 @@ GtkWidget* ui_subcontainer_create( int rowspacing, int margin); -UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame); -void ui_frame_container_add(UiContainer *ct, GtkWidget *widget); - -GtkWidget* ui_box_set_margin(GtkWidget *box, int margin); +GtkWidget* ui_gtk_set_margin(GtkWidget *widget, int margin, int margin_left, int margin_right, int margin_top, int margin_bottom); UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, UiSubContainerType type); -UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type); -void ui_box_container_add(UiContainer *ct, GtkWidget *widget); +UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type); +void ui_box_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout); GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing); -UiContainer* ui_grid_container( +UiContainerX* ui_grid_container( UiObject *obj, GtkWidget *grid, UiBool def_hexpand, UiBool def_vexpand, UiBool def_hfill, UiBool def_vfill); -void ui_grid_container_add(UiContainer *ct, GtkWidget *widget); +void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout); -UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame); -void ui_frame_container_add(UiContainer *ct, GtkWidget *widget); +UiContainerX* ui_frame_container(UiObject *obj, GtkWidget *frame); +void ui_frame_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout); -UiContainer* ui_expander_container(UiObject *obj, GtkWidget *expander); -void ui_expander_container_add(UiContainer *ct, GtkWidget *widget); +UiContainerX* ui_expander_container(UiObject *obj, GtkWidget *expander); +void ui_expander_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout); -UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow); -void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget); +UiContainerX* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow); +void ui_scrolledwindow_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout); -UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview); -void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget); +UiContainerX* ui_tabview_container(UiObject *obj, GtkWidget *tabview); +void ui_tabview_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout); -UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max, int init); -void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget); +UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init); +UiContainerX* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiSplitPane *data); +void ui_splitpane_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout); +int64_t ui_splitpane_get(UiInteger *i); +void ui_splitpane_set(UiInteger *i, int64_t value); UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview); void ui_gtk_notebook_select_tab(GtkWidget *widget, int tab); #if GTK_CHECK_VERSION(3, 10, 0) -UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar); -void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget); +UiContainerX* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar); +void ui_headerbar_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout); #endif -UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar); -void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget); +UiContainerX* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar); +void ui_headerbar_fallback_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout); #ifdef __cplusplus } diff --git a/ui/gtk/display.c b/ui/gtk/display.c index 40fcde1..1f8c39e 100644 --- a/ui/gtk/display.c +++ b/ui/gtk/display.c @@ -47,8 +47,6 @@ static void set_alignment(GtkWidget *widget, float xalign, float yalign) { } UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs *args) { - UiObject* current = uic_current_obj(obj); - const char *css_class = NULL; char *markup = NULL; if(args->label) { @@ -101,11 +99,11 @@ UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs *args) { case UI_ALIGN_DEFAULT: break; case UI_ALIGN_LEFT: set_alignment(widget, 0, .5); break; case UI_ALIGN_RIGHT: set_alignment(widget, 1, .5); break; - case UI_ALIGN_CENTER: break; // TODO + case UI_ALIGN_CENTER: set_alignment(widget, .5, .5); break; } - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); if(var) { UiString* value = (UiString*)var->value; value->obj = widget; @@ -113,8 +111,9 @@ UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs *args) { value->set = ui_label_set; } - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, widget); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); return widget; } @@ -147,10 +146,12 @@ void ui_label_set(UiString *s, const char *value) { } } +/* UIWIDGET ui_space_deprecated(UiObject *obj) { GtkWidget *widget = gtk_label_new(""); - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, widget); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); return widget; } @@ -161,12 +162,14 @@ UIWIDGET ui_separator_deprecated(UiObject *obj) { #else GtkWidget *widget = gtk_hseparator_new(); #endif - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, widget); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); return widget; } - +*/ + /* ------------------------- progress bar ------------------------- */ typedef struct UiProgressBarRange { @@ -175,8 +178,6 @@ typedef struct UiProgressBarRange { } UiProgressBarRange; UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args) { - UiObject* current = uic_current_obj(obj); - GtkWidget *progressbar = gtk_progress_bar_new(); if(args->max > args->min) { UiProgressBarRange *range = malloc(sizeof(UiProgressBarRange)); @@ -191,7 +192,7 @@ UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args) { } - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_DOUBLE); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_DOUBLE); if(var && var->value) { UiDouble *value = var->value; value->get = ui_progressbar_get; @@ -200,8 +201,9 @@ UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs *args) { ui_progressbar_set(value, value->value); } - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, progressbar); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, progressbar, &layout); return progressbar; } @@ -229,11 +231,9 @@ void ui_progressbar_set(UiDouble *d, double value) { /* ------------------------- progress spinner ------------------------- */ UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs *args) { - UiObject* current = uic_current_obj(obj); - GtkWidget *spinner = gtk_spinner_new(); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_INTEGER); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); if(var && var->value) { UiInteger *value = var->value; value->get = ui_spinner_get; @@ -242,8 +242,9 @@ UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs *args ui_spinner_set(value, value->value); } - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, spinner); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, spinner, &layout); return spinner; } diff --git a/ui/gtk/draw_cairo.c b/ui/gtk/draw_cairo.c index fc85c32..c067bc0 100644 --- a/ui/gtk/draw_cairo.c +++ b/ui/gtk/draw_cairo.c @@ -35,29 +35,29 @@ #if GTK_MAJOR_VERSION >= 3 -static void ui_drawingarea_draw( - GtkDrawingArea *area, - cairo_t *cr, - int width, - int height, - gpointer data) -{ +void ui_drawingarea_draw(UiDrawingArea *drawingarea, cairo_t *cr, int width, int height) { UiCairoGraphics g; g.g.width = width; g.g.height = height; - g.widget = GTK_WIDGET(area); + g.widget = drawingarea->widget;; g.cr = cr; - UiDrawEvent *event = data; - UiEvent ev; - ev.obj = event->obj; - ev.window = event->obj->window; - ev.document = event->obj->ctx->document; + UiEvent event; + event.obj = drawingarea->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = 0; + event.intval = 0; + event.set = 0; - event->callback(&ev, &g.g, event->userdata); + if(drawingarea->draw) { + drawingarea->draw(&event, &g.g, drawingarea->drawdata); + } } #endif +/* #if UI_GTK3 gboolean ui_drawingarea_expose(GtkWidget *w, cairo_t *cr, void *data) { int width = gtk_widget_get_allocated_width(w); @@ -85,9 +85,11 @@ gboolean ui_canvas_expose(GtkWidget *w, GdkEventExpose *e, void *data) { return FALSE; } #endif +*/ // function from graphics.h +/* void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event) { #if GTK_MAJOR_VERSION >= 4 gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(widget), ui_drawingarea_draw, event, NULL); @@ -103,7 +105,7 @@ void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event) { event); #endif } - +*/ PangoContext *ui_get_pango_context(UiGraphics *g) { UiCairoGraphics *gr = (UiCairoGraphics*)g; @@ -130,7 +132,7 @@ void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2) { cairo_stroke(gr->cr); } -void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, int fill) { +void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, UiBool fill) { UiCairoGraphics *gr = (UiCairoGraphics*)g; cairo_set_line_width(gr->cr, 1); cairo_rectangle(gr->cr, x + 0.5, y + 0.5 , w, h); diff --git a/ui/gtk/draw_cairo.h b/ui/gtk/draw_cairo.h index 6be96a7..a1ae8d7 100644 --- a/ui/gtk/draw_cairo.h +++ b/ui/gtk/draw_cairo.h @@ -41,7 +41,7 @@ typedef struct UiCairoGraphics { cairo_t *cr; } UiCairoGraphics; -// ui_canvas_expose +void ui_drawingarea_draw(UiDrawingArea *drawingarea, cairo_t *cr, int width, int height); #ifdef __cplusplus } diff --git a/ui/gtk/entry.c b/ui/gtk/entry.c index 3c83f11..b411191 100644 --- a/ui/gtk/entry.c +++ b/ui/gtk/entry.c @@ -35,28 +35,36 @@ #include "entry.h" -UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) { - double min = 0; - double max = 1000; - - UiObject* current = uic_current_obj(obj); +UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) { + 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, current->ctx, args->intvalue, NULL, UI_VAR_INTEGER); + 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, current->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE); + 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, current->ctx, args->rangevalue, NULL, UI_VAR_RANGE); + var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE); + vartype = UI_VAR_RANGE; } } - if(var && var->type == UI_VAR_RANGE) { + if(vartype == UI_VAR_RANGE) { UiRange *r = var->value; min = r->min; max = r->max; @@ -72,11 +80,16 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) { GtkWidget *spin = gtk_spin_button_new_with_range(min, max, args->step); ui_set_name_and_style(spin, args->name, args->style_class); ui_set_widget_groups(obj->ctx, spin, args->groups); + + if(args->width > 0) { + gtk_widget_set_size_request(spin, args->width, -1); + } + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), args->digits); UiObserver **obs = NULL; if(var) { double value = 0; - switch(var->type) { + switch(vartype) { default: break; case UI_VAR_INTEGER: { UiInteger *i = var->value; @@ -129,8 +142,9 @@ UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) { G_CALLBACK(ui_destroy_vardata), event); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, spin); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, spin, &layout); return spin; } diff --git a/ui/gtk/graphics.c b/ui/gtk/graphics.c index 6883575..33fd186 100644 --- a/ui/gtk/graphics.c +++ b/ui/gtk/graphics.c @@ -33,19 +33,88 @@ #include "container.h" #include "../common/object.h" -UIWIDGET ui_drawingarea(UiObject *obj, ui_drawfunc f, void *userdata) { +#if GTK_CHECK_VERSION(3, 0, 0) +#include "draw_cairo.h" +#endif + +static void destroy_drawingarea(GtkWidget *widget, UiDrawingArea *drawingarea) { + free(drawingarea); +} + +static void drawingarea_draw(UiDrawingArea *drawingarea, cairo_t *cr, int width, int height) { + UiEvent event; + event.obj = drawingarea->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = 0; + event.intval = 0; + event.set = 0; + + +} + +#if GTK_CHECK_VERSION(4, 0, 0) +static void drawfunc( + GtkDrawingArea *area, + cairo_t *cr, + int width, + int height, + gpointer userdata) +{ + ui_drawingarea_draw(userdata, cr, width, height); +} +#elif GTK_CHECK_VERSION(3, 0, 0) +gboolean draw_callback(GtkWidget *widget, cairo_t *cr, UiDrawingArea *drawingarea) { + int width = gtk_widget_get_allocated_width(widget); + int height = gtk_widget_get_allocated_height(widget); + ui_drawingarea_draw(drawingarea, cr, width, height); + return FALSE; +} +#endif + +UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args) { GtkWidget *widget = gtk_drawing_area_new(); + ui_set_name_and_style(widget, args->name, args->style_class); - if(f) { - UiDrawEvent *event = malloc(sizeof(UiDrawEvent)); - event->obj = obj; - event->callback = f; - event->userdata = userdata; - ui_connect_draw_handler(widget, event); - } +#if GTK_CHECK_VERSION(4, 0, 0) + gtk_drawing_area_set_content_width(GTK_DRAWING_AREA(widget), args->width > 0 ? args->width : 100); + gtk_drawing_area_set_content_height(GTK_DRAWING_AREA(widget), args->height > 0 ? args->height : 100); +#else + int w = args->width > 0 ? args->width : 100; + int h = args->height > 0 ? args->height : 100; + gtk_widget_set_size_request(widget, w, h); +#endif + + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); + + UiDrawingArea *drawingarea = malloc(sizeof(UiDrawingArea)); + drawingarea->obj = obj; + drawingarea->widget = widget; + drawingarea->draw = args->draw; + drawingarea->drawdata = args->drawdata; + drawingarea->onclick = args->onclick; + drawingarea->onclickdata = args->onclickdata; + drawingarea->onmotion = args->onmotion; + drawingarea->onmotiondata = args->onmotiondata; + +#if GTK_CHECK_VERSION(4, 0, 0) + gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(widget), drawfunc, drawingarea, NULL); +#elif GTK_CHECK_VERSION(3, 0, 0) + g_signal_connect( + widget, + "draw", + G_CALLBACK(draw_callback), + NULL); +#endif - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, widget); + g_signal_connect( + widget, + "destroy", + G_CALLBACK(destroy_drawingarea), + drawingarea); return widget; } @@ -142,7 +211,7 @@ void ui_text_setstringl(UiTextLayout *layout, char *str, int len) { pango_layout_set_text(layout->layout, str, len); } -void ui_text_setfont(UiTextLayout *layout, char *font, int size) { +void ui_text_setfont(UiTextLayout *layout, const char *font, int size) { PangoFontDescription *fontDesc; fontDesc = pango_font_description_from_string(font); pango_font_description_set_size(fontDesc, size * PANGO_SCALE); diff --git a/ui/gtk/graphics.h b/ui/gtk/graphics.h index 67616bd..fb89539 100644 --- a/ui/gtk/graphics.h +++ b/ui/gtk/graphics.h @@ -36,18 +36,24 @@ extern "C" { #endif -typedef struct UiDrawEvent { - ui_drawfunc callback; - UiObject *obj; - void *userdata; -} UiDrawEvent; + +typedef struct UiDrawingArea { + UiObject *obj; + GtkWidget *widget; + ui_drawfunc draw; + void *drawdata; + ui_callback onclick; + void *onclickdata; + ui_callback onmotion; + void *onmotiondata; +} UiDrawingArea; struct UiTextLayout { PangoLayout *layout; }; // implemented in draw_*.h -void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event); +//void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event); PangoContext *ui_get_pango_context(UiGraphics *g); #ifdef __cplusplus diff --git a/ui/gtk/headerbar.c b/ui/gtk/headerbar.c index e1b80b5..0a9bd3e 100644 --- a/ui/gtk/headerbar.c +++ b/ui/gtk/headerbar.c @@ -31,21 +31,70 @@ #include "button.h" #include "menu.h" +#include "../ui/properties.h" + #if GTK_CHECK_VERSION(3, 10, 0) -void ui_fill_headerbar(UiObject *obj, GtkWidget *headerbar) { +void ui_fill_headerbar(UiObject *obj, GtkWidget *sidebar_headerbar, GtkWidget *main_headerbar, GtkWidget *right_headerbar) { + CxList *sidebar_left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_LEFT); + CxList *sidebar_right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_RIGHT); + CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); - ui_headerbar_add_items(obj, headerbar, left_defaults, UI_TOOLBAR_LEFT); - ui_headerbar_add_items(obj, headerbar, center_defaults, UI_TOOLBAR_CENTER); - + CxList *rightpanel_left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_LEFT); + CxList *rightpanel_center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_CENTER); + CxList *rightpanel_right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_RIGHT); + UiToolbarMenuItem *appmenu = uic_get_appmenu(); - if(appmenu) { - ui_add_headerbar_menu(headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT); + const char *appmenu_pos_str = ui_get_property("ui.gtk.window.appmenu.position"); + int appmenu_pos = UI_TOOLBAR_RIGHT; + if(sidebar_headerbar) { + appmenu_pos = UI_TOOLBAR_SIDEBAR_RIGHT; + } else if(right_headerbar) { + appmenu_pos = UI_TOOLBAR_RIGHTPANEL_RIGHT; + } + if(appmenu_pos_str) { + if(!strcmp(appmenu_pos_str, "sidebar") && sidebar_headerbar) { + appmenu_pos = UI_TOOLBAR_SIDEBAR_RIGHT; + } else if(!strcmp(appmenu_pos_str, "main")) { + appmenu_pos = UI_TOOLBAR_RIGHT; + } else if(!strcmp(appmenu_pos_str, "rightpanel") && right_headerbar) { + appmenu_pos = UI_TOOLBAR_RIGHTPANEL_RIGHT; + } + } + + // main toolbar + ui_headerbar_add_items(obj, main_headerbar, left_defaults, UI_TOOLBAR_LEFT); + ui_headerbar_add_items(obj, main_headerbar, center_defaults, UI_TOOLBAR_CENTER); + + if(appmenu && appmenu_pos == UI_TOOLBAR_RIGHT) { + ui_add_headerbar_menu(main_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT); + } + ui_headerbar_add_items(obj, main_headerbar, right_defaults, UI_TOOLBAR_RIGHT); + + // sidebar + if(sidebar_headerbar) { + // ui_headerbar_add_items pos parameter uses only UI_TOOLBAR_LEFT, UI_TOOLBAR_CENTER, UI_TOOLBAR_RIGHT + ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_left_defaults, UI_TOOLBAR_LEFT); + + if(appmenu && appmenu_pos == UI_TOOLBAR_SIDEBAR_RIGHT) { + ui_add_headerbar_menu(sidebar_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT); + } + ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_right_defaults, UI_TOOLBAR_RIGHT); + } + + // right panel + if(right_headerbar) { + ui_headerbar_add_items(obj, right_headerbar, rightpanel_left_defaults, UI_TOOLBAR_LEFT); + ui_headerbar_add_items(obj, right_headerbar, rightpanel_center_defaults, UI_TOOLBAR_CENTER); + + if(appmenu && appmenu_pos == UI_TOOLBAR_RIGHTPANEL_RIGHT) { + ui_add_headerbar_menu(right_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT); + } + ui_headerbar_add_items(obj, right_headerbar, rightpanel_right_defaults, UI_TOOLBAR_RIGHT); } - ui_headerbar_add_items(obj, headerbar, right_defaults, UI_TOOLBAR_RIGHT); } static void create_item(UiObject *obj, GtkWidget *headerbar, GtkWidget *box, UiToolbarItemI *i, enum UiToolbarPos pos) { @@ -114,8 +163,9 @@ void ui_add_headerbar_item( UiObject *obj, enum UiToolbarPos pos) { - GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.onclick, item->args.onclickdata, 0, FALSE); + GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.tooltip, item->args.onclick, item->args.onclickdata, 0, FALSE); ui_set_widget_groups(obj->ctx, button, item->args.groups); + ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states); WIDGET_ADD_CSS_CLASS(button, "flat"); headerbar_add(headerbar, box, button, pos); } @@ -129,8 +179,9 @@ void ui_add_headerbar_toggleitem( { GtkWidget *button = gtk_toggle_button_new(); ui_set_widget_groups(obj->ctx, button, item->args.groups); + ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states); WIDGET_ADD_CSS_CLASS(button, "flat"); - ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0); + ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.tooltip, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0); headerbar_add(headerbar, box, button, pos); } @@ -145,6 +196,7 @@ void ui_add_headerbar_menu( #if GTK_MAJOR_VERSION >= 4 GtkWidget *menubutton = gtk_menu_button_new(); + ui_set_widget_visibility_states(obj->ctx, menubutton, item->args.visibility_states); if(item->args.label) { gtk_menu_button_set_label(GTK_MENU_BUTTON(menubutton), item->args.label); } @@ -162,9 +214,10 @@ void ui_add_headerbar_menu( gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(menubutton), G_MENU_MODEL(menu)); #else GtkWidget *menubutton = gtk_menu_button_new(); - - // TODO - + GtkWidget *menu = gtk_menu_new(); + ui_add_menu_items(menu, 0, &item->menu, obj); + gtk_widget_show_all(menu); + gtk_menu_button_set_popup(GTK_MENU_BUTTON(menubutton), menu); #endif diff --git a/ui/gtk/headerbar.h b/ui/gtk/headerbar.h index acea9fe..93028ea 100644 --- a/ui/gtk/headerbar.h +++ b/ui/gtk/headerbar.h @@ -58,7 +58,7 @@ extern "C" { #endif #endif -void ui_fill_headerbar(UiObject *obj, GtkWidget *headerbar); +void ui_fill_headerbar(UiObject *obj, GtkWidget *sidebar_headerbar, GtkWidget *main_headerbar, GtkWidget *right_headerbar); void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos); diff --git a/ui/gtk/image.c b/ui/gtk/image.c index 425af82..e60dbdd 100644 --- a/ui/gtk/image.c +++ b/ui/gtk/image.c @@ -64,8 +64,6 @@ static gboolean imageviewer_draw(GtkWidget *widget, cairo_t *cr, gpointer userda #endif UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args) { - UiObject *current = uic_current_obj(obj); - GtkWidget *drawingarea = gtk_drawing_area_new(); GtkWidget *toplevel; GtkWidget *widget = drawingarea; @@ -111,7 +109,7 @@ UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args) { g_object_set_data_full(G_OBJECT(drawingarea), "uiimageviewer", imgviewer, (GDestroyNotify)imageviewer_destroy); - UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_GENERIC); + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC); imgviewer->var = var; imgviewer->widget = drawingarea; @@ -187,8 +185,9 @@ UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args) { ui_widget_set_contextmenu(widget, menu); } - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, toplevel); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, toplevel, &layout); return toplevel; } diff --git a/ui/gtk/list.c b/ui/gtk/list.c index be9c00a..f793b2c 100644 --- a/ui/gtk/list.c +++ b/ui/gtk/list.c @@ -39,21 +39,22 @@ #include #include "list.h" +#include "button.h" #include "icon.h" #include "menu.h" #include "dnd.h" -void* ui_strmodel_getvalue(void *elm, int column) { - return column == 0 ? elm : NULL; +static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata; + return getvalue(elm, col); } -static void* model_getvalue(UiModel *model, UiList *list, void *elm, int row, int col, UiBool *freeResult) { - if(model->getvalue2) { - return model->getvalue2(list, elm, row, col, model->getvalue2data, freeResult); - } else if(model->getvalue) { - return model->getvalue(elm, col); - } +static void* str_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + return elm; +} + +static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { return NULL; } @@ -74,6 +75,42 @@ static void listview_copy_static_elements(UiListView *listview, char **elm, size } } +static UiListView* create_listview(UiObject *obj, UiListArgs *args) { + UiListView *tableview = malloc(sizeof(UiListView)); + memset(tableview, 0, sizeof(UiListView)); + tableview->obj = obj; + tableview->model = args->model; + tableview->onactivate = args->onactivate; + tableview->onactivatedata = args->onactivatedata; + tableview->onselection = args->onselection; + tableview->onselectiondata = args->onselectiondata; + tableview->ondragstart = args->ondragstart; + tableview->ondragstartdata = args->ondragstartdata; + tableview->ondragcomplete = args->ondragcomplete; + tableview->ondragcompletedata = args->ondragcompletedata; + tableview->ondrop = args->ondrop; + tableview->ondropdata = args->ondropdata; + tableview->selection.count = 0; + tableview->selection.rows = NULL; + tableview->current_row = -1; + tableview->getstyle = args->getstyle; + tableview->getstyledata = args->getstyledata; + tableview->onsave = args->onsave; + tableview->onsavedata = args->onsavedata; + + if(args->getvalue2) { + tableview->getvalue = args->getvalue2; + tableview->getvaluedata = args->getvalue2data; + } else if(args->getvalue) { + tableview->getvalue = getvalue_wrapper; + tableview->getvaluedata = (void*)args->getvalue; + } else { + tableview->getvalue = null_getvalue; + } + + return tableview; +} + #if GTK_CHECK_VERSION(4, 10, 0) @@ -108,6 +145,58 @@ ObjWrapper* obj_wrapper_new(void* data, int i) { /* END GObject wrapper for generic pointers */ +typedef struct UiCellEntry { + GtkEntry *entry; + UiListView *listview; + char *previous_value; + int row; + int col; +} UiCellEntry; + +static void cell_save_value(UiCellEntry *data, int restore) { + if(data->listview && data->listview->onsave) { + UiVar *var = data->listview->var; + UiList *list = var ? var->value : NULL; + const char *str = ENTRY_GET_TEXT(data->entry); + UiCellValue value; + value.string = str; + value.type = UI_STRING_EDITABLE; + if(data->listview->onsave(list, data->row, data->col, &value, data->listview->onsavedata)) { + free(data->previous_value); + data->previous_value = strdup(str); + } else if(restore) { + ENTRY_SET_TEXT(data->entry, data->previous_value); + } + } +} + +static void cell_entry_leave_focus( + GtkEventControllerFocus *self, + UiCellEntry *data) +{ + // TODO: use a different singal to track focus + // we only want to call cell_save_value, when another entry is selected, + // not when the window loses focus or something like that + cell_save_value(data, TRUE); +} + +static void cell_entry_destroy(GtkWidget *object, UiCellEntry *data) { + free(data->previous_value); + free(data); +} + +static void cell_entry_unmap(GtkWidget *w, UiCellEntry *data) { + const char *text = ENTRY_GET_TEXT(w); + cell_save_value(data, FALSE); +} + +static void cell_entry_activate( + GtkEntry *self, + UiCellEntry *data) +{ + cell_save_value(data, TRUE); +} + static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { UiColData *col = userdata; UiModel *model = col->listview->model; @@ -124,25 +213,125 @@ static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, } else if(type == UI_ICON) { GtkWidget *image = gtk_image_new(); gtk_list_item_set_child(item, image); - } else { + } else if(type == UI_STRING_EDITABLE) { + GtkWidget *textfield = gtk_entry_new(); + gtk_widget_add_css_class(textfield, "ui-table-entry"); + gtk_list_item_set_child(item, textfield); + + UiCellEntry *entry_data = malloc(sizeof(UiCellEntry)); + entry_data->entry = GTK_ENTRY(textfield); + entry_data->listview = NULL; + entry_data->previous_value = NULL; + entry_data->col = 0; + entry_data->row = 0; + g_object_set_data(G_OBJECT(textfield), "ui_entry_data", entry_data); + + g_signal_connect( + textfield, + "destroy", + G_CALLBACK(cell_entry_destroy), + entry_data); + g_signal_connect( + textfield, + "activate", + G_CALLBACK(cell_entry_activate), + entry_data); + g_signal_connect( + textfield, + "unmap", + G_CALLBACK(cell_entry_unmap), + entry_data); + + GtkEventController *focus_controller = gtk_event_controller_focus_new(); + g_signal_connect(focus_controller, "leave", G_CALLBACK(cell_entry_leave_focus), entry_data); + gtk_widget_add_controller(textfield, focus_controller); + } else if(type == UI_BOOL_EDITABLE) { + GtkWidget *checkbox = gtk_check_button_new(); + gtk_list_item_set_child(item, checkbox); + }else { GtkWidget *label = gtk_label_new(NULL); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_list_item_set_child(item, label); } } -static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { +PangoAttrList* textstyle2pangoattributes(UiTextStyle style) { + PangoAttrList *attr = pango_attr_list_new(); + + if(style.text_style & UI_TEXT_STYLE_BOLD) { + pango_attr_list_insert(attr, pango_attr_weight_new(PANGO_WEIGHT_BOLD)); + } + if(style.text_style & UI_TEXT_STYLE_ITALIC) { + pango_attr_list_insert(attr, pango_attr_style_new(PANGO_STYLE_ITALIC)); + } + if(style.text_style & UI_TEXT_STYLE_UNDERLINE) { + pango_attr_list_insert(attr, pango_attr_underline_new(PANGO_UNDERLINE_SINGLE)); + } + + // foreground color, convert from 8bit to 16bit + guint16 r = (guint16)style.fg.red * 257; + guint16 g = (guint16)style.fg.green * 257; + guint16 b = (guint16)style.fg.blue * 257; + pango_attr_list_insert(attr, pango_attr_foreground_new(r, g, b)); + + return attr; +} + +static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, gpointer userdata) { UiColData *col = userdata; UiList *list = col->listview->var ? col->listview->var->value : NULL; - + UiListView *listview = col->listview; + ObjWrapper *obj = gtk_list_item_get_item(item); UiModel *model = col->listview->model; UiModelType type = model->types[col->model_column]; + // cache the GtkListItem + CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int)); + UiRowItems *row = cxMapGet(listview->bound_rows, row_key); + if(row) { + if(row->items[col->model_column] == NULL) { + row->bound++; + } + } else { + row = calloc(1, sizeof(UiRowItems) + listview->numcolumns * sizeof(GtkListItem*)); + cxMapPut(listview->bound_rows, row_key, row); + row->bound = 1; + } + row->items[col->model_column] = item; + UiBool freevalue = FALSE; - void *data = model_getvalue(model, list, obj->data, obj->i, col->data_column, &freevalue); + void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue); GtkWidget *child = gtk_list_item_get_child(item); + PangoAttrList *attributes = NULL; + UiTextStyle style = { 0, 0 }; + if(listview->getstyle) { + // query current row style, if it wasn't already queried + if(obj->i != listview->current_row) { + listview->current_row = obj->i; + listview->row_style = (UiTextStyle){ 0, 0 }; + listview->apply_row_style = listview->getstyle(list, obj->data, obj->i, -1, listview->getstyledata, &listview->row_style); + style = listview->row_style; + if(listview->apply_row_style) { + pango_attr_list_unref(listview->current_row_attributes); + listview->current_row_attributes = textstyle2pangoattributes(style); + } + } + + int style_col = col->data_column; + if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) { + style_col++; // col->data_column is the icon, we need the next col for the label + } + + // get the column style + if(listview->getstyle(list, obj->data, obj->i, style_col, listview->getstyledata, &style)) { + attributes = textstyle2pangoattributes(style); + } else if(listview->apply_row_style) { + attributes = listview->current_row_attributes; + } + } + switch(type) { case UI_STRING_FREE: { freevalue = TRUE; @@ -152,6 +341,7 @@ static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, if(freevalue) { free(data); } + gtk_label_set_attributes(GTK_LABEL(child), attributes); break; } case UI_INTEGER: { @@ -159,6 +349,7 @@ static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, char buf[32]; snprintf(buf, 32, "%d", (int)intvalue); gtk_label_set_label(GTK_LABEL(child), buf); + gtk_label_set_attributes(GTK_LABEL(child), attributes); break; } case UI_ICON: { @@ -172,7 +363,7 @@ static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, } case UI_ICON_TEXT_FREE: { - void *data2 = model_getvalue(model, list, obj->data, obj->i, col->data_column+1, &freevalue); + void *data2 = listview->getvalue(list, obj->data, obj->i, col->data_column+1, listview->getvaluedata, &freevalue); if(type == UI_ICON_TEXT_FREE) { freevalue = TRUE; } @@ -184,70 +375,96 @@ static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, } if(data2 && label) { gtk_label_set_label(GTK_LABEL(label), data2); + gtk_label_set_attributes(GTK_LABEL(label), attributes); } if(freevalue) { free(data2); } break; } + case UI_STRING_EDITABLE: { + UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data"); + if(entry) { + entry->listview = col->listview; + entry->row = obj->i; + entry->col = col->data_column; + entry->previous_value = strdup(data); + } + ENTRY_SET_TEXT(child, data); + break; + } + case UI_BOOL_EDITABLE: { + intptr_t i = (intptr_t)data; + gtk_check_button_set_active(GTK_CHECK_BUTTON(child), (gboolean)i); + break; + } + } + + if(attributes != listview->current_row_attributes) { + pango_attr_list_unref(attributes); } } +static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) { + ObjWrapper *obj = gtk_list_item_get_item(item); + UiListView *listview = col->listview; + CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int)); + UiRowItems *row = cxMapGet(listview->bound_rows, row_key); + if(row) { + row->items[col->model_column] = NULL; + row->bound--; + if(row->bound == 0) { + cxMapRemove(listview->bound_rows, row_key); + } + } // else: should not happen + + GtkWidget *child = gtk_list_item_get_child(item); + UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data"); + if(entry) { + cell_save_value(entry, FALSE); + entry->listview = NULL; + free(entry->previous_value); + entry->previous_value = NULL; + } else if(GTK_IS_CHECK_BUTTON(child)) { + + } +} + + static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) { GtkSelectionModel *selection_model; if(multiselection) { selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(G_LIST_MODEL(liststore))); } else { selection_model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(liststore))); + gtk_single_selection_set_can_unselect(GTK_SINGLE_SELECTION(selection_model), TRUE); + gtk_single_selection_set_autoselect(GTK_SINGLE_SELECTION(selection_model), FALSE); } g_signal_connect(selection_model, "selection-changed", G_CALLBACK(ui_listview_selection_changed), listview); return selection_model; } -static UiListView* create_listview(UiObject *obj, UiListArgs *args) { - UiListView *tableview = malloc(sizeof(UiListView)); - memset(tableview, 0, sizeof(UiListView)); - tableview->obj = obj; - tableview->model = args->model; - tableview->onactivate = args->onactivate; - tableview->onactivatedata = args->onactivatedata; - tableview->onselection = args->onselection; - tableview->onselectiondata = args->onselectiondata; - tableview->ondragstart = args->ondragstart; - tableview->ondragstartdata = args->ondragstartdata; - tableview->ondragcomplete = args->ondragcomplete; - tableview->ondragcompletedata = args->ondragcompletedata; - tableview->ondrop = args->ondrop; - tableview->ondropdata = args->ondropdata; - tableview->selection.count = 0; - tableview->selection.rows = NULL; - return tableview; -} - UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { - UiObject* current = uic_current_obj(obj); - // to simplify things and share code with ui_table_create, we also // use a UiModel for the listview UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } args->model = model; GListStore *ls = g_list_store_new(G_TYPE_OBJECT); UiListView *listview = create_listview(obj, args); + if(!args->getvalue && !args->getvalue2) { + listview->getvalue = str_getvalue; + } + listview->numcolumns = 1; listview->columns = malloc(sizeof(UiColData)); listview->columns->listview = listview; listview->columns->data_column = 0; listview->columns->model_column = 0; + listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128); + listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free; + GtkListItemFactory *factory = gtk_signal_list_item_factory_new(); g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns); g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns); @@ -255,7 +472,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection); GtkWidget *view = gtk_list_view_new(GTK_SELECTION_MODEL(selection_model), factory); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); // init listview listview->widget = view; @@ -280,7 +497,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { ui_update_liststore(ls, list); } else if (args->static_elements && args->static_nelm > 0) { listview_copy_static_elements(listview, args->static_elements, args->static_nelm); - listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + listview->getvalue = str_getvalue; // force string values ui_update_liststore_static(ls, listview->elements, listview->nelm); } @@ -304,48 +521,58 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS SCROLLEDWINDOW_SET_CHILD(scroll_area, view); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, scroll_area); + if(args->width > 0 || args->height > 0) { + int width = args->width; + int height = args->height; + if(width == 0) { + width = -1; + } + if(height == 0) { + height = -1; + } + gtk_widget_set_size_request(scroll_area, width, height); + } - // ct->current should point to view, not scroll_area, to make it possible - // to add a context menu - current->container->current = view; + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, scroll_area, &layout); return scroll_area; } UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { - UiObject* current = uic_current_obj(obj); - // to simplify things and share code with ui_tableview_create, we also // use a UiModel for the listview UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } args->model = model; GListStore *ls = g_list_store_new(G_TYPE_OBJECT); UiListView *listview = create_listview(obj, args); + if(!args->getvalue && !args->getvalue2) { + listview->getvalue = str_getvalue; + } + + listview->numcolumns = 1; listview->columns = malloc(sizeof(UiColData)); listview->columns->listview = listview; listview->columns->data_column = 0; listview->columns->model_column = 0; + listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128); + listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free; + GtkListItemFactory *factory = gtk_signal_list_item_factory_new(); g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns); g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns); GtkWidget *view = gtk_drop_down_new(G_LIST_MODEL(ls), NULL); gtk_drop_down_set_factory(GTK_DROP_DOWN(view), factory); + if(args->width > 0) { + gtk_widget_set_size_request(view, args->width, -1); + } - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); // init listview listview->widget = view; @@ -370,7 +597,7 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { ui_update_liststore(ls, list); } else if (args->static_elements && args->static_nelm > 0) { listview_copy_static_elements(listview, args->static_elements, args->static_nelm); - listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + listview->getvalue = str_getvalue; // force string values ui_update_liststore_static(ls, listview->elements, listview->nelm); } @@ -380,8 +607,10 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { } // add widget to parent - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, view); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, view, &layout); + return view; } @@ -395,8 +624,6 @@ void ui_combobox_select(UIWIDGET dropdown, int index) { } UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { - UiObject* current = uic_current_obj(obj); - GListStore *ls = g_list_store_new(G_TYPE_OBJECT); //g_list_store_append(ls, v1); @@ -407,7 +634,7 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args->multiselection); GtkWidget *view = gtk_column_view_new(GTK_SELECTION_MODEL(selection_model)); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); // init tableview tableview->widget = view; @@ -426,6 +653,10 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { int columns = model ? model->columns : 0; tableview->columns = calloc(columns, sizeof(UiColData)); + tableview->numcolumns = columns; + + tableview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128); + tableview->bound_rows->collection.simple_destructor = (cx_destructor_func)free; int addi = 0; for(int i=0;icontainer->add(current->container, scroll_area); + if(args->width > 0 || args->height > 0) { + int width = args->width; + int height = args->height; + if(width == 0) { + width = -1; + } + if(height == 0) { + height = -1; + } + gtk_widget_set_size_request(scroll_area, width, height); + } - // ct->current should point to view, not scroll_area, to make it possible - // to add a context menu - current->container->current = view; + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, scroll_area, &layout); return scroll_area; } @@ -625,14 +865,19 @@ void ui_listview_update2(UiList *list, int i) { } else { void *value = list->get(list, i); if(value) { - ObjWrapper *obj = obj_wrapper_new(value, i); - // TODO: if index i is selected, the selection is lost - // is it possible to update the item without removing it? - int count = g_list_model_get_n_items(G_LIST_MODEL(view->liststore)); - if(count <= i) { - g_list_store_splice(view->liststore, i, 0, (void **)&obj, 1); - } else { - g_list_store_splice(view->liststore, i, 1, (void **)&obj, 1); + ObjWrapper *obj = g_list_model_get_item(G_LIST_MODEL(view->liststore), i); + if(obj) { + obj->data = value; + } + + CxHashKey row_key = cx_hash_key(&i, sizeof(int)); + UiRowItems *row = cxMapGet(view->bound_rows, row_key); + if(row) { + for(int c=0;cnumcolumns;c++) { + if(row->items[c] != NULL) { + column_factory_bind(NULL, row->items[c], &view->columns[c]); + } + } } } } @@ -695,15 +940,42 @@ void ui_combobox_setselection(UiList *list, UiListSelection selection) { #else -static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *model, UiList *list, void *elm, int row) { +static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIter *iter, UiList *list, void *elm, int row) { + UiModel *model = listview->model; + ui_getstylefunc getstyle = listview->getstyle; + + // get the row style + UiBool style_set = FALSE; + UiTextStyle style = { 0, 0 }; + if(getstyle) { + style_set = getstyle(list, elm, row, -1, listview->getstyledata, &style); + } + // set column values int c = 0; for(int i=0;icolumns;i++,c++) { UiBool freevalue = FALSE; - void *data = model_getvalue(model, list, elm, row, c, &freevalue); + void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue); + + UiModelType type = model->types[i]; + + if(getstyle) { + // in case the column is icon+text, only get a style for the text column + int style_col = c; + if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) { + style_col++; + } + + // Get the individual column style + // The column style overrides the row style, however if no column style + // is provided, we stick with the row style + if(getstyle(list, elm, row, style_col, listview->getstyledata, &style)) { + style_set = TRUE; + } + } GValue value = G_VALUE_INIT; - switch(model->types[i]) { + switch(type) { case UI_STRING_FREE: { freevalue = TRUE; } @@ -765,7 +1037,7 @@ static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *mod c++; freevalue = FALSE; - char *str = model_getvalue(model, list, elm, row, c, &freevalue); + char *str = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue); g_value_init(&value, G_TYPE_STRING); g_value_set_string(&value, str); if(model->types[i] == UI_ICON_TEXT_FREE || freevalue) { @@ -776,12 +1048,64 @@ static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *mod } gtk_list_store_set_value(store, iter, c, &value); + + if(style_set) { + int soff = listview->style_offset + i*6; + + GValue style_set_value = G_VALUE_INIT; + g_value_init(&style_set_value, G_TYPE_BOOLEAN); + g_value_set_boolean(&style_set_value, TRUE); + gtk_list_store_set_value(store, iter, soff, &style_set_value); + + GValue style_weight_value = G_VALUE_INIT; + g_value_init(&style_weight_value, G_TYPE_INT); + if(style.text_style & UI_TEXT_STYLE_BOLD) { + g_value_set_int(&style_weight_value, 600); + } else { + g_value_set_int(&style_weight_value, 400); + } + gtk_list_store_set_value(store, iter, soff + 1, &style_weight_value); + + GValue style_underline_value = G_VALUE_INIT; + g_value_init(&style_underline_value, G_TYPE_INT); + if(style.text_style & UI_TEXT_STYLE_UNDERLINE) { + g_value_set_int(&style_underline_value, PANGO_UNDERLINE_SINGLE); + } else { + g_value_set_int(&style_underline_value, PANGO_UNDERLINE_NONE); + } + gtk_list_store_set_value(store, iter, soff + 2, &style_underline_value); + + GValue style_italic_value = G_VALUE_INIT; + g_value_init(&style_italic_value, G_TYPE_INT); + if(style.text_style & UI_TEXT_STYLE_ITALIC) { + g_value_set_int(&style_italic_value, PANGO_STYLE_ITALIC); + } else { + g_value_set_int(&style_italic_value, PANGO_STYLE_NORMAL); + } + gtk_list_store_set_value(store, iter, soff + 3, &style_italic_value); + + GValue style_fgset_value = G_VALUE_INIT; + g_value_init(&style_fgset_value, G_TYPE_BOOLEAN); + g_value_set_boolean(&style_fgset_value, style.fg_set); + gtk_list_store_set_value(store, iter, soff + 4, &style_fgset_value); + + if(style.fg_set) { + char buf[8]; + snprintf(buf, 8, "#%02X%02X%02X", (int)style.fg.red, (int)style.fg.green, (int)style.fg.blue); + + GValue style_fg_value = G_VALUE_INIT; + g_value_init(&style_fg_value, G_TYPE_STRING); + g_value_set_string(&style_fg_value, buf); + gtk_list_store_set_value(store, iter, soff + 5, &style_fg_value); + } + } } } -static GtkListStore* create_list_store(UiList *list, UiModel *model) { +static GtkListStore* create_list_store(UiListView *listview, UiList *list) { + UiModel *model = listview->model; int columns = model->columns; - GType types[2*columns]; + GType *types = calloc(columns*8, sizeof(GType)); int c = 0; for(int i=0;itypes[i]) { @@ -796,8 +1120,18 @@ static GtkListStore* create_list_store(UiList *list, UiModel *model) { } } } + int s = 0; + for(int i=0;istyle_offset+s] = G_TYPE_BOOLEAN; s++; // *-set + types[listview->style_offset+s] = G_TYPE_INT; s++; // weight + types[listview->style_offset+s] = G_TYPE_INT; s++; // underline + types[listview->style_offset+s] = G_TYPE_INT; s++; // style + types[listview->style_offset+s] = G_TYPE_BOOLEAN; s++; // foreground-set + types[listview->style_offset+s] = G_TYPE_STRING; s++; // foreground + } - GtkListStore *store = gtk_list_store_newv(c, types); + GtkListStore *store = gtk_list_store_newv(c+s, types); + free(types); if(list) { void *elm = list->first(list); @@ -807,7 +1141,7 @@ static GtkListStore* create_list_store(UiList *list, UiModel *model) { GtkTreeIter iter; gtk_list_store_insert (store, &iter, -1); - update_list_row(store, &iter, model, list, elm, i++); + update_list_row(listview, store, &iter, list, elm, i++); // next row elm = list->next(list); @@ -819,8 +1153,6 @@ static GtkListStore* create_list_store(UiList *list, UiModel *model) { UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { - UiObject* current = uic_current_obj(obj); - // create treeview GtkWidget *view = gtk_tree_view_new(); ui_set_name_and_style(view, args->name, args->style_class); @@ -841,36 +1173,30 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { #endif UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); - - UiList *list = var ? var->value : NULL; - GtkListStore *listmodel = create_list_store(list, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - g_object_unref(listmodel); - - UiListView *listview = malloc(sizeof(UiListView)); - memset(listview, 0, sizeof(UiListView)); - listview->obj = obj; - listview->widget = view; - listview->var = var; + UiListView *listview = create_listview(obj, args); + listview->style_offset = 1; + if(!args->getvalue && !args->getvalue2) { + listview->getvalue = str_getvalue; + } listview->model = model; - listview->selection.count = 0; - listview->selection.rows = NULL; g_signal_connect( view, "destroy", G_CALLBACK(ui_listview_destroy), listview); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); + + // init listview + listview->widget = view; + listview->var = var; + + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(listview, list); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); + g_object_unref(listmodel); + // bind var list->update = ui_listview_update; list->getselection = ui_listview_getselection; @@ -920,12 +1246,21 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS SCROLLEDWINDOW_SET_CHILD(scroll_area, view); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, scroll_area, FALSE); + if(args->width > 0 || args->height > 0) { + int width = args->width; + int height = args->height; + if(width == 0) { + width = -1; + } + if(height == 0) { + height = -1; + } + gtk_widget_set_size_request(scroll_area, width, height); + } - // ct->current should point to view, not scroll_area, to make it possible - // to add a context menu - current->container->current = view; + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, scroll_area, &layout); return scroll_area; } @@ -942,18 +1277,28 @@ void ui_combobox_select(UIWIDGET dropdown, int index) { } UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { - UiObject* current = uic_current_obj(obj); - // create treeview GtkWidget *view = gtk_tree_view_new(); UiModel *model = args->model; int columns = model ? model->columns : 0; + // find the last data column index int addi = 0; - for(int i=0;itypes[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) { + addi++; + } + } + style_offset = i+addi; + + // create columns and init cell renderers + addi = 0; + for(i=0;itypes[i] == UI_ICON_TEXT) { + if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) { column = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(column, model->titles[i]); @@ -964,8 +1309,21 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { gtk_tree_view_column_pack_start(column, iconrenderer, FALSE); - gtk_tree_view_column_add_attribute(column, iconrenderer, "pixbuf", i); - gtk_tree_view_column_add_attribute(column, textrenderer, "text", i+1); + gtk_tree_view_column_add_attribute(column, iconrenderer, "pixbuf", addi + i); + gtk_tree_view_column_add_attribute(column, textrenderer, "text", addi + i+1); + + if(args->getstyle) { + int soff = style_offset + i*6; + gtk_tree_view_column_add_attribute(column, textrenderer, "weight-set", soff); + gtk_tree_view_column_add_attribute(column, textrenderer, "underline-set", soff); + gtk_tree_view_column_add_attribute(column, textrenderer, "style-set", soff); + + gtk_tree_view_column_add_attribute(column, textrenderer, "weight", soff + 1); + gtk_tree_view_column_add_attribute(column, textrenderer, "underline", soff + 2); + gtk_tree_view_column_add_attribute(column, textrenderer, "style", soff + 3); + gtk_tree_view_column_add_attribute(column, textrenderer, "foreground-set", soff + 4); + gtk_tree_view_column_add_attribute(column, textrenderer, "foreground", soff + 5); + } addi++; } else if (model->types[i] == UI_ICON) { @@ -977,13 +1335,26 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { i + addi, NULL); } else { - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( model->titles[i], - renderer, + textrenderer, "text", i + addi, NULL); + + if(args->getstyle) { + int soff = style_offset + i*6; + gtk_tree_view_column_add_attribute(column, textrenderer, "weight-set", soff); + gtk_tree_view_column_add_attribute(column, textrenderer, "underline-set", soff); + gtk_tree_view_column_add_attribute(column, textrenderer, "style-set", soff); + + gtk_tree_view_column_add_attribute(column, textrenderer, "weight", soff + 1); + gtk_tree_view_column_add_attribute(column, textrenderer, "underline", soff + 2); + gtk_tree_view_column_add_attribute(column, textrenderer, "style", soff + 3); + gtk_tree_view_column_add_attribute(column, textrenderer, "foreground-set", soff + 4); + gtk_tree_view_column_add_attribute(column, textrenderer, "foreground", soff + 5); + } } int colsz = model->columnsize[i]; @@ -1004,38 +1375,27 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { #endif - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); - - UiList *list = var ? var->value : NULL; - GtkListStore *listmodel = create_list_store(list, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - g_object_unref(listmodel); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL); //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL); // add TreeView as observer to the UiList to update the TreeView if the // data changes - UiListView *tableview = malloc(sizeof(UiListView)); - memset(tableview, 0, sizeof(UiListView)); - tableview->obj = obj; + UiListView *tableview = create_listview(obj, args); tableview->widget = view; - tableview->var = var; - tableview->model = model; - tableview->ondragstart = args->ondragstart; - tableview->ondragstartdata = args->ondragstartdata; - tableview->ondragcomplete = args->ondragcomplete; - tableview->ondragcompletedata = args->ondragcompletedata; - tableview->ondrop = args->ondrop; - tableview->ondropdata = args->ondropdata; - tableview->selection.count = 0; - tableview->selection.rows = NULL; + tableview->style_offset = style_offset; g_signal_connect( view, "destroy", G_CALLBACK(ui_listview_destroy), tableview); + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(tableview, list); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); + g_object_unref(listmodel); + // bind var list->update = ui_listview_update; list->getselection = ui_listview_getselection; @@ -1088,6 +1448,18 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS SCROLLEDWINDOW_SET_CHILD(scroll_area, view); + if(args->width > 0 || args->height > 0) { + int width = args->width; + int height = args->height; + if(width == 0) { + width = -1; + } + if(height == 0) { + height = -1; + } + gtk_widget_set_size_request(scroll_area, width, height); + } + if(args->contextmenu) { UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, scroll_area); #if GTK_MAJOR_VERSION >= 4 @@ -1097,12 +1469,9 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { #endif } - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, scroll_area, FALSE); - - // ct->current should point to view, not scroll_area, to make it possible - // to add a context menu - current->container->current = view; + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, scroll_area, &layout); return scroll_area; } @@ -1112,7 +1481,7 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { void ui_listview_update(UiList *list, int i) { UiListView *view = list->obj; if(i < 0) { - GtkListStore *store = create_list_store(list, view->model); + GtkListStore *store = create_list_store(view, list); gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store)); g_object_unref(G_OBJECT(store)); } else { @@ -1120,7 +1489,7 @@ void ui_listview_update(UiList *list, int i) { GtkTreeModel *store = gtk_tree_view_get_model(GTK_TREE_VIEW(view->widget)); GtkTreeIter iter; if(gtk_tree_model_iter_nth_child(store, &iter, NULL, i)) { - update_list_row(GTK_LIST_STORE(store), &iter, view->model, list, elm, i); + update_list_row(view, GTK_LIST_STORE(store), &iter, list, elm, i); } } } @@ -1148,48 +1517,45 @@ void ui_listview_setselection(UiList *list, UiListSelection selection) { /* --------------------------- ComboBox --------------------------- */ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { - UiObject* current = uic_current_obj(obj); - - UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; + GtkWidget *combobox = gtk_combo_box_new(); + if(args->width > 0) { + gtk_widget_set_size_request(combobox, args->width, -1); } - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); - - GtkWidget *combobox = ui_create_combobox(obj, model, var, args->static_elements, args->static_nelm, args->onactivate, args->onactivatedata); ui_set_name_and_style(combobox, args->name, args->style_class); ui_set_widget_groups(obj->ctx, combobox, args->groups); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, combobox, FALSE); - current->container->current = combobox; - return combobox; -} - -GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata) { - GtkWidget *combobox = gtk_combo_box_new(); - - UiListView *uicbox = malloc(sizeof(UiListView)); - memset(uicbox, 0, sizeof(UiListView)); - uicbox->obj = obj; - uicbox->widget = combobox; + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, combobox, &layout); - UiList *list = var ? var->value : NULL; - GtkListStore *listmodel = create_list_store(list, model); + UiListView *listview = create_listview(obj, args); + listview->widget = combobox; + listview->style_offset = 1; + listview->model = ui_model(obj->ctx, UI_STRING, "", -1); + g_signal_connect( + combobox, + "destroy", + G_CALLBACK(ui_listview_destroy), + listview); - if(!list && elm && nelm > 0) { - listview_copy_static_elements(uicbox, elm, nelm); - for(int i=0;ictx, obj->ctx, args->list, args->varname, UI_VAR_LIST); + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(listview, list); + if(var) { + listview->var = var; + list->update = ui_combobox_modelupdate; + list->getselection = ui_combobox_getselection; + list->setselection = ui_combobox_setselection; + list->obj = listview; + list->update(list, -1); + } else if(args->static_nelm > 0) { + listview_copy_static_elements(listview, args->static_elements, args->static_nelm); + for(int i=0;istatic_nelm;i++) { GtkTreeIter iter; GValue value = G_VALUE_INIT; gtk_list_store_insert(listmodel, &iter, -1); g_value_init(&value, G_TYPE_STRING); - g_value_set_string(&value, uicbox->elements[i]); + g_value_set_string(&value, listview->elements[i]); gtk_list_store_set_value(listmodel, &iter, 0, &value); } } @@ -1199,23 +1565,6 @@ GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char ** g_object_unref(listmodel); } - uicbox->var = var; - uicbox->model = model; - - g_signal_connect( - combobox, - "destroy", - G_CALLBACK(ui_combobox_destroy), - uicbox); - - // bind var - if(list) { - list->update = ui_combobox_modelupdate; - list->getselection = ui_combobox_getselection; - list->setselection = ui_combobox_setselection; - list->obj = uicbox; - } - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE); gtk_cell_layout_set_attributes( @@ -1227,13 +1576,13 @@ GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char ** gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0); // add callback - if(f) { + if(args->onactivate) { UiEventData *event = ui_malloc(obj->ctx, sizeof(UiEventData)); event->obj = obj; - event->userdata = udata; - event->callback = f; + event->userdata = args->onactivatedata; + event->callback = args->onactivate; event->value = 0; - event->customdata = uicbox; + event->customdata = listview; g_signal_connect( combobox, @@ -1268,7 +1617,7 @@ void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) { void ui_combobox_modelupdate(UiList *list, int i) { UiListView *view = list->obj; - GtkListStore *store = create_list_store(view->var->value, view->model); + GtkListStore *store = create_list_store(view, list); gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(store)); g_object_unref(store); } @@ -1704,27 +2053,28 @@ void ui_listview_destroy(GtkWidget *w, UiListView *v) { } #if GTK_CHECK_VERSION(4, 10, 0) free(v->columns); + pango_attr_list_unref(v->current_row_attributes); + cxMapFree(v->bound_rows); #endif free(v->selection.rows); free(v); } -void ui_combobox_destroy(GtkWidget *w, UiListView *v) { - if(v->var) { - ui_destroy_boundvar(v->obj->ctx, v->var); - } - if(v->elements) { - for(int i=0;inelm;i++) { - free(v->elements[i]); - } - free(v->elements); - } - free(v); -} - /* ------------------------------ Source List ------------------------------ */ +static ui_sourcelist_update_func sourcelist_update_finished_callback; + +void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb) { + sourcelist_update_finished_callback = cb; +} + +static void ui_sourcelist_update_finished(void) { + if(sourcelist_update_finished_callback) { + sourcelist_update_finished_callback(); + } +} + static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) { cxListFree(v->sublists); free(v); @@ -1751,7 +2101,7 @@ static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpo if(sublist->separator) { GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); gtk_list_box_row_set_header(row, separator); - } else if(sublist->header) { + } else if(sublist->header && !listbox->header_is_item) { GtkWidget *header = gtk_label_new(sublist->header); gtk_widget_set_halign(header, GTK_ALIGN_START); if(row == listbox->first_row) { @@ -1801,21 +2151,19 @@ static void add_sublist(UiListBox *uilistbox, CxList *sublists, UiSubList *subli uisublist.listbox = uilistbox; uisublist.userdata = sublist->userdata; uisublist.index = cxListSize(sublists); - + uisublist.startpos = 0; + cxListAdd(sublists, &uisublist); + // bind UiList UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(sublists)-1); - UiList *list = uisublist.var->value; - if(list) { + if(uisublist.var && uisublist.var->value) { + UiList *list = uisublist.var->value; list->obj = sublist_ptr; list->update = ui_listbox_list_update; } - - cxListAdd(sublists, &uisublist); } UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { - UiObject* current = uic_current_obj(obj); - #ifdef UI_GTK3 GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL); #else @@ -1834,12 +2182,14 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { ui_set_name_and_style(listbox, args->name, args->style_class); ui_set_widget_groups(obj->ctx, listbox, args->groups); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, scroll_area); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, scroll_area, &layout); UiListBox *uilistbox = malloc(sizeof(UiListBox)); uilistbox->obj = obj; uilistbox->listbox = GTK_LIST_BOX(listbox); + uilistbox->header_is_item = args->header_is_item; uilistbox->getvalue = args->getvalue; uilistbox->getvaluedata = args->getvaluedata; uilistbox->onactivate = args->onactivate; @@ -1868,7 +2218,7 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { // fill items ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists)); } else { - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST); if(var) { UiList *list = var->value; list->obj = uilistbox; @@ -1883,6 +2233,11 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox); g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox); + if(args->contextmenu) { + UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, listbox); + ui_widget_set_contextmenu(listbox, menu); + } + // signals g_signal_connect( listbox, @@ -1952,22 +2307,82 @@ void ui_listbox_update(UiListBox *listbox, int from, int to) { } // reload sublist + sublist->startpos = pos; ui_listbox_update_sublist(listbox, sublist, pos); pos += sublist->numitems; } + + ui_sourcelist_update_finished(); +} + +static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) { + UiListBoxSubList *sublist = data->customdata0; + + 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.eventdatatype = UI_EVENT_DATA_SUBLIST; + event.intval = data->value0; + event.set = ui_get_setop(); + + if(data->callback2) { + data->callback2(&event, data->userdata2); + } + + 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)); + } } -static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) { +#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")) { + g_object_set_data(G_OBJECT(button), "ui-button-invisible", NULL); + 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) { + UiBool is_header = index < 0; + GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); if(item->icon) { GtkWidget *icon = ICON_IMAGE(item->icon); BOX_ADD(hbox, icon); } GtkWidget *label = gtk_label_new(item->label); + if(is_header) { + WIDGET_ADD_CSS_CLASS(label, "ui-listbox-header-row"); + } gtk_widget_set_halign(label, GTK_ALIGN_START); BOX_ADD_EXPAND(hbox, label); - // TODO: badge, button - GtkWidget *row = gtk_list_box_row_new(); + if(item->badge) { + + } LISTBOX_ROW_SET_CHILD(row, hbox); // signals @@ -1983,6 +2398,9 @@ static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *subli event->userdata2 = listbox->onbuttonclickdata; event->value0 = index; + // TODO: semi-memory leak when listbox_fill_row is called again for the same row + // each row update will create a new UiEventDataExt object and a separate destroy handler + g_signal_connect( row, "destroy", @@ -1991,9 +2409,137 @@ static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *subli g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event); - return row; + // badge + if(item->badge) { + GtkWidget *badge = gtk_label_new(item->badge); + WIDGET_ADD_CSS_CLASS(badge, "ui-badge"); +#if GTK_CHECK_VERSION(3, 14, 0) + gtk_widget_set_valign(badge, GTK_ALIGN_CENTER); + BOX_ADD(hbox, badge); +#else + GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0); + gtk_container_add(GTK_CONTAINER(align), badge); + BOX_ADD(hbox, align); +#endif + } + // button + if(item->button_icon || item->button_label) { + GtkWidget *button = gtk_button_new(); + gtk_button_set_label(GTK_BUTTON(button), item->button_label); + ui_button_set_icon_name(button, item->button_icon); + WIDGET_ADD_CSS_CLASS(button, "flat"); + BOX_ADD(hbox, button); + g_signal_connect( + button, + "clicked", + G_CALLBACK(listbox_button_clicked), + event + ); + gtk_widget_set_visible(button, FALSE); + + g_object_set_data(G_OBJECT(row), "ui-listbox-row-button", button); + + // menu + if(item->button_menu) { + UIMENU menu = ui_contextmenu_create(item->button_menu, listbox->obj, button); + event->customdata3 = menu; +#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); + } + } } +static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist, int index) { + int header_row = listbox->header_is_item && sublist->header ? 1 : 0; + GtkListBoxRow *row = gtk_list_box_get_row_at_index(listbox->listbox, sublist->startpos + index + header_row); + if(!row) { + return; + } + UiList *list = sublist->var->value; + if(!list) { + return; + } + + void *elm = list->get(list, index); + UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; + if(listbox->getvalue) { + listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata); + } else { + item.label = strdup(elm); + } + + LISTBOX_ROW_REMOVE_CHILD(row); + + listbox_fill_row(listbox, GTK_WIDGET(row), sublist, &item, index); + + // cleanup + free(item.label); + free(item.icon); + free(item.button_label); + free(item.button_icon); + free(item.badge); +} + +static void listbox_row_on_enter(GtkWidget *row) { + GtkWidget *button = g_object_get_data(G_OBJECT(row), "ui-listbox-row-button"); + if(button) { + gtk_widget_set_visible(button, TRUE); + } +} + +static void listbox_row_on_leave(GtkWidget *row) { + GtkWidget *button = g_object_get_data(G_OBJECT(row), "ui-listbox-row-button"); + if(button) { + if(!g_object_get_data(G_OBJECT(button), "ui-button-popup")) { + gtk_widget_set_visible(button, FALSE); + } else { + g_object_set_data(G_OBJECT(button), "ui-button-invisible", (void*)1); + } + } +} + +#if GTK_CHECK_VERSION(4, 0, 0) +static void listbox_row_enter( + GtkEventControllerMotion* self, + gdouble x, + gdouble y, + GtkWidget *row) +{ + listbox_row_on_enter(row); +} + +static void listbox_row_leave( + GtkEventControllerMotion* self, + GtkWidget *row) +{ + listbox_row_on_leave(row); +} +#else +static gboolean listbox_row_enter( + GtkWidget *row, + GdkEventCrossing event, + gpointer user_data) +{ + listbox_row_on_enter(row); + return FALSE; +} + + +static gboolean listbox_row_leave( + GtkWidget *row, + GdkEventCrossing *event, + gpointer user_data) +{ + listbox_row_on_leave(row); + return FALSE; +} + +#endif + void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) { // clear sublist CxIterator r = cxListIterator(sublist->widgets); @@ -2005,13 +2551,40 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si sublist->numitems = 0; // create items for each UiList element + if(!sublist->var) { + return; + } UiList *list = sublist->var->value; if(!list) { return; } - size_t index = 0; + int index = 0; void *elm = list->first(list); + void *first = elm; + + if(sublist->header && !listbox->header_is_item && !elm) { + // empty row for header + GtkWidget *row = gtk_list_box_row_new(); + cxListAdd(sublist->widgets, row); + g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); + g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist); + //intptr_t rowindex = listbox_insert_index + index; + //g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); + gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index); + sublist->numitems = 1; + return; + } + + int first_index = 0; + int header_row = 0; + if(listbox->header_is_item && sublist->header) { + index = -1; + first_index = -1; + header_row = 1; + elm = sublist->header; + } + while(elm) { UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; if(listbox->getvalue) { @@ -2020,9 +2593,25 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si item.label = strdup(elm); } + if(item.label == NULL && index == -1 && sublist->header) { + item.label = strdup(sublist->header); + } + // create listbox item - GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index); - if(index == 0) { + GtkWidget *row = gtk_list_box_row_new(); +#if GTK_CHECK_VERSION(4, 0, 0) + GtkEventController *motion_controller = gtk_event_controller_motion_new(); + gtk_widget_add_controller(GTK_WIDGET(row), motion_controller); + g_signal_connect(motion_controller, "enter", G_CALLBACK(listbox_row_enter), row); + g_signal_connect(motion_controller, "leave", G_CALLBACK(listbox_row_leave), row); +#else + gtk_widget_set_events(GTK_WIDGET(row), GDK_POINTER_MOTION_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + g_signal_connect(row, "enter-notify-event", G_CALLBACK(listbox_row_enter), NULL); + g_signal_connect(row, "leave-notify-event", G_CALLBACK(listbox_row_leave), NULL); +#endif + + listbox_fill_row(listbox, row, sublist, &item, index); + if(index == first_index) { // first row in the sublist, set ui_listbox data to the row // which is then used by the headerfunc g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); @@ -2033,9 +2622,9 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si listbox->first_row = GTK_LIST_BOX_ROW(row); } } - intptr_t rowindex = listbox_insert_index + index; - g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); - gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index); + //intptr_t rowindex = listbox_insert_index + index; + //g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); + gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index + header_row); cxListAdd(sublist->widgets, row); // cleanup @@ -2046,7 +2635,7 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si free(item.badge); // next row - elm = list->next(list); + elm = index >= 0 ? list->next(list) : first; index++; } @@ -2055,6 +2644,19 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si void ui_listbox_list_update(UiList *list, int i) { UiListBoxSubList *sublist = list->obj; + if(i < 0) { + ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos); + size_t pos = 0; + CxIterator it = cxListIterator(sublist->listbox->sublists); + cx_foreach(UiListBoxSubList *, ls, it) { + ls->startpos = pos; + pos += ls->numitems; + } + } else { + update_sublist_item(sublist->listbox, sublist, i); + } + + ui_sourcelist_update_finished(); } void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { @@ -2069,7 +2671,7 @@ void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user 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.row_data = eventdata.row_index >= 0 ? eventdata.list->get(eventdata.list, eventdata.row_index) : NULL; eventdata.event_data = data->customdata2; UiEvent event; diff --git a/ui/gtk/list.h b/ui/gtk/list.h index bd490ab..1aaf89b 100644 --- a/ui/gtk/list.h +++ b/ui/gtk/list.h @@ -40,17 +40,36 @@ extern "C" { typedef struct UiColData UiColData; +#if GTK_CHECK_VERSION(4, 10, 0) +typedef struct UiRowItems { + int bound; + GtkListItem *items[]; +} UiRowItems; +#endif + typedef struct UiListView { UiObject *obj; GtkWidget *widget; UiVar *var; UiModel *model; + ui_getvaluefunc2 getvalue; + void *getvaluedata; + ui_getstylefunc getstyle; + void *getstyledata; char **elements; size_t nelm; + int current_row; + UiTextStyle row_style; + UiBool apply_row_style; #if GTK_CHECK_VERSION(4, 10, 0) + CxMap *bound_rows; GListStore *liststore; GtkSelectionModel *selectionmodel; UiColData *columns; + int numcolumns; + PangoAttrList *current_row_attributes; +#else + int style_offset; #endif ui_callback onactivate; void *onactivatedata; @@ -62,6 +81,8 @@ typedef struct UiListView { void *ondragcompletedata; ui_callback ondrop; void *ondropdata; + ui_list_savefunc onsave; + void *onsavedata; UiListSelection selection; } UiListView; @@ -91,6 +112,7 @@ typedef struct UiListBoxSubList { UiListBox *listbox; void *userdata; size_t index; + size_t startpos; } UiListBoxSubList; struct UiListBox { @@ -104,6 +126,8 @@ struct UiListBox { ui_callback onbuttonclick; void *onbuttonclickdata; GtkListBoxRow *first_row; + UiBool header_is_item; + UiSubListEventData current_eventdata; }; diff --git a/ui/gtk/menu.c b/ui/gtk/menu.c index 521bf91..74597ae 100644 --- a/ui/gtk/menu.c +++ b/ui/gtk/menu.c @@ -33,6 +33,7 @@ #include "menu.h" #include "toolkit.h" +#include "widget.h" #include "../common/context.h" #include "../common/menu.h" #include "../common/types.h" @@ -42,6 +43,7 @@ #include #include +#include #if GTK_MAJOR_VERSION <= 3 @@ -135,44 +137,6 @@ void add_menuitem_widget(GtkWidget *parent, int index, UiMenuItemI *item, UiObje } } -/* -void add_menuitem_st_widget( - GtkWidget *parent, - int index, - UiMenuItemI *item, - UiObject *obj) -{ - UiStMenuItem *i = (UiStMenuItem*)item; - - GtkWidget *widget = gtk_image_menu_item_new_from_stock(i->stockid, obj->ctx->accel_group); - - if(i->callback != NULL) { - UiEventData *event = malloc(sizeof(UiEventData)); - event->obj = obj; - event->userdata = i->userdata; - event->callback = i->callback; - event->value = 0; - - g_signal_connect( - widget, - "activate", - G_CALLBACK(ui_menu_event_wrapper), - event); - g_signal_connect( - widget, - "destroy", - G_CALLBACK(ui_destroy_userdata), - event); - } - - gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget); - - if(i->groups) { - uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, i->groups); - } -} -*/ - void add_menuseparator_widget( GtkWidget *parent, int index, @@ -214,25 +178,6 @@ void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject * // TODO } -/* -void add_checkitemnv_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { - UiCheckItemNV *ci = (UiCheckItemNV*)item; - GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label); - gtk_menu_shell_append(GTK_MENU_SHELL(p), widget); - - UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER); - if(var) { - UiInteger *value = var->value; - value->obj = widget; - value->get = ui_checkitem_get; - value->set = ui_checkitem_set; - value = 0; - } else { - // TODO: error - } -} -*/ - static void menuitem_list_remove_binding(void *obj) { UiActiveMenuItemList *ls = obj; UiList *list = ls->var->value; @@ -261,8 +206,8 @@ void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObje ls->oldcount = 0; ls->getvalue = il->getvalue; - //UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); - UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST); + UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); + //UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST); ls->var = var; if(var) { UiList *list = var->value; @@ -339,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, @@ -364,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) { @@ -468,6 +422,9 @@ void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj) GMenu *section = NULL; while(it) { if(it->type == UI_MENU_SEPARATOR) { + if(section) { + g_object_unref(section); + } section = g_menu_new(); g_menu_append_section(parent, NULL, G_MENU_MODEL(section)); index++; @@ -481,6 +438,9 @@ void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj) } it = it->next; } + if(section) { + g_object_unref(section); + } } void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { @@ -488,6 +448,7 @@ void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *ob GMenu *menu = g_menu_new(); ui_gmenu_add_menu_items(menu, 0, mi, obj); g_menu_append_submenu(parent, mi->label, G_MENU_MODEL(menu)); + g_object_unref(menu); } static void action_enable(GSimpleAction *action, int enabled) { @@ -499,6 +460,7 @@ void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject GSimpleAction *action = g_simple_action_new(item->id, NULL); g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); + g_object_unref(action); if(i->groups) { CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups); @@ -529,7 +491,8 @@ void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject } char action_name[32]; - snprintf(action_name, 32, "win.%s", item->id); + snprintf(action_name, 32, "win.%s", item->id); + g_menu_append(parent, i->label, action_name); } @@ -543,8 +506,161 @@ void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *ob // TODO } -void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + + +typedef struct UiCallbackData { + ui_callback callback; + void *userdata; +} UiCallbackData; + +static void radiogroup_remove_binding(void *obj) { + UiMenuRadioGroup *group = obj; + if(group->var) { + UiInteger *i = group->var->value; + CxList *bindings = i->obj; + if(bindings) { + (void)cxListFindRemove(bindings, obj); + } + } +} + +static void stateful_action_notify_group(UiMenuRadioGroup *group, UiInteger *i) { + UiEvent event; + event.obj = group->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = 0; + event.intval = (int)i->value; + event.set = ui_get_setop(); + + CxIterator iter = cxListIterator(group->callbacks); + cx_foreach(UiCallbackData *, cb, iter) { + if(cb->callback) { + cb->callback(&event, cb->userdata); + } + } + + UiObserver *obs = i->observers; + while(obs) { + if(obs->callback) { + obs->callback(&event, obs->data); + } + obs = obs->next; + } +} + +static void ui_action_set_state(UiInteger *i, int64_t value) { + i->value = value; + CxList *bindings = i->obj; + CxIterator iter = cxListIterator(bindings); + cx_foreach(UiMenuRadioGroup *, group, iter) { + char buf[32]; + snprintf(buf, 32, "%d", (int)value); + GVariant *state = g_variant_new_string(buf); + g_action_change_state(G_ACTION(group->action), state); + stateful_action_notify_group(group, i); + } +} + +static int64_t ui_action_get_state(UiInteger *i) { + return i->value; +} + +static UiMenuRadioGroup* create_radio_group(UiObject *obj, UiMenuRadioItem *item, GSimpleAction *action) { + UiMenuRadioGroup *group = cxZalloc(obj->ctx->allocator, sizeof(UiMenuRadioGroup)); + group->callbacks = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(UiCallbackData), 8); + group->var = uic_create_var(ui_global_context(), item->varname, UI_VAR_INTEGER); + group->obj = obj; + group->action = action; + if(group->var) { + UiInteger *i = group->var->value; + CxList *bindings = i->obj; + if(!bindings) { + bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS); + i->obj = bindings; + i->set = ui_action_set_state; + i->get = ui_action_get_state; + } + cxListAdd(bindings, group); + // the destruction of the toplevel obj must remove the binding + uic_context_add_destructor(obj->ctx, radiogroup_remove_binding, group); + } + return group; +} + +static void stateful_action_activate( + GSimpleAction *action, + GVariant *state, + UiMenuRadioGroup *group) +{ + g_simple_action_set_state(action, state); + + UiVar *var = group->var; + if(!var) { + return; + } + UiInteger *i = var->value; + + gsize len; + const char *s = g_variant_get_string(state, &len); + cxstring value = cx_strn(s, len); + int v; + if(!cx_strtoi(value, &v, 10)) { + i->value = v; + } + + CxList *bindings = i->obj; + CxIterator iter = cxListIterator(bindings); + cx_foreach(UiMenuRadioGroup *, gr, iter) { + stateful_action_notify_group(gr, i); + } +} + + +void ui_gmenu_add_radioitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuRadioItem *i = (UiMenuRadioItem*)item; + + if(!i->varname) { + return; + } + + // All radio buttons with the name varname use the same GAction + UiMenuRadioGroup *group = NULL; + GAction *action = g_action_map_lookup_action(obj->ctx->action_map, i->varname); + if(!action) { + GVariant *state = g_variant_new_string("0"); + GSimpleAction *newAction = g_simple_action_new_stateful(i->varname, G_VARIANT_TYPE_STRING, state); + g_action_map_add_action(obj->ctx->action_map, G_ACTION(newAction)); + g_object_unref(newAction); + + group = create_radio_group(obj, i, newAction); + g_object_set_data(G_OBJECT(newAction), "ui_radiogroup", group); + + g_signal_connect( + newAction, + "change-state", + G_CALLBACK(stateful_action_activate), + group); + } else { + group = g_object_get_data(G_OBJECT(action), "ui_radiogroup"); + if(!group) { + fprintf(stderr, "Error: missing ui_radiogroup property for action %s\n", i->varname); + return; // error, should not happen + } + } + + size_t item_index = cxListSize(group->callbacks); + UiCallbackData cb; + cb.callback = i->callback; + cb.userdata = i->userdata; + cxListAdd(group->callbacks, &cb); + + + cxmutstr action_name = cx_asprintf("win.%s::%d", i->varname, (int)item_index); + g_menu_append(parent, i->label, action_name.ptr); + free(action_name.ptr); } static void menuitem_list_remove_binding(void *obj) { @@ -576,8 +692,13 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject ls->oldcount = 0; ls->getvalue = il->getvalue; - //UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); - UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST); + GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i")); + g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); + g_object_unref(action); + snprintf(ls->action, 32, "win.%s", item->id); + + UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); + //UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST); ls->var = var; if(var) { UiList *list = var->value; @@ -604,10 +725,6 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject ls->callback = il->callback; ls->userdata = il->userdata; - GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i")); - g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); - snprintf(ls->action, 32, "win.%s", item->id); - UiEventData *event = malloc(sizeof(UiEventData)); event->obj = obj; @@ -639,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) { @@ -650,6 +773,10 @@ void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* par UiVar *var = event->customdata; UiList *list = var->value; + if(!event->callback) { + return; + } + UiEvent evt; evt.obj = event->obj; evt.window = event->obj->window; @@ -687,6 +814,7 @@ void ui_update_gmenu_item_list(UiActiveGMenuItemList *list) { GVariant *v = g_variant_new("i", i); g_menu_item_set_action_and_target_value(item, list->action, v); g_menu_insert_item(list->menu, list->index+i, item); + g_object_unref(item); elm = ui_list_next(ls); i++; @@ -706,6 +834,7 @@ UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, GtkWidget *w GMenu *menu = g_menu_new(); ui_gmenu_add_menu_items(menu, 0, builder->menus_begin, obj); GtkWidget *contextmenu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu)); + g_object_unref(menu); gtk_popover_set_has_arrow(GTK_POPOVER(contextmenu), FALSE); gtk_widget_set_halign(contextmenu, GTK_ALIGN_START); gtk_widget_set_parent(GTK_WIDGET(contextmenu), widget); diff --git a/ui/gtk/menu.h b/ui/gtk/menu.h index dc6240c..a7f65e7 100644 --- a/ui/gtk/menu.h +++ b/ui/gtk/menu.h @@ -98,6 +98,14 @@ struct UiActiveGMenuItemList { void *userdata; }; +typedef struct UiMenuRadioGroup { + UiObject *obj; + CxList *callbacks; + UiVar *var; + GSimpleAction *action; +} UiMenuRadioGroup; + + void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj); void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj); diff --git a/ui/gtk/range.c b/ui/gtk/range.c index 45c5bfc..e33b24a 100644 --- a/ui/gtk/range.c +++ b/ui/gtk/range.c @@ -76,8 +76,10 @@ static UIWIDGET ui_scrollbar(UiObject *obj, UiOrientation orientation, UiRange * event); } - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, scrollbar); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + //UiLayout layout = UI_ARGS2LAYOUT(args); + UiLayout layout = {0}; + ct->add(ct, scrollbar, &layout); return scrollbar; } @@ -126,7 +128,7 @@ void ui_scrollbar_setextent(UiRange *range, double extent) { #else gtk_adjustment_set_page_size(a, extent); #endif -#if GTK_MAJOR_VERSION * 100 + GTK_MIMOR_VERSION < 318 +#if !GTK_CHECK_VERSION(3, 18, 0) gtk_adjustment_changed(a); #endif range->extent = extent; diff --git a/ui/gtk/text.c b/ui/gtk/text.c index 9ea0c4c..0859642 100644 --- a/ui/gtk/text.c +++ b/ui/gtk/text.c @@ -108,8 +108,7 @@ static GtkTextBuffer* create_textbuffer(UiTextArea *textarea) { } UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) { - UiObject* current = uic_current_obj(obj); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_TEXT); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_TEXT); GtkWidget *text_area = gtk_text_view_new(); ui_set_name_and_style(text_area, args->name, args->style_class); @@ -145,6 +144,18 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) { GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS SCROLLEDWINDOW_SET_CHILD(scroll_area, text_area); + if(args->width > 0 || args->height > 0) { + int width = args->width; + int height = args->height; + if(width == 0) { + width = -1; + } + if(height == 0) { + height = -1; + } + gtk_widget_set_size_request(scroll_area, width, height); + } + // font and padding //PangoFontDescription *font; //font = pango_font_description_from_string("Monospace"); @@ -155,8 +166,9 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) { gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text_area), 2); // add - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, scroll_area); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, scroll_area, &layout); // bind value if(var) { @@ -598,8 +610,7 @@ static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool passwor ui_set_name_and_style(textfield, args->name, args->style_class); ui_set_widget_groups(obj->ctx, textfield, args->groups); - UiObject* current = uic_current_obj(obj); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); UiTextField *uitext = malloc(sizeof(UiTextField)); uitext->obj = obj; @@ -616,10 +627,11 @@ static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool passwor uitext); if(args->width > 0) { - // TODO: gtk4 -#if GTK_MAJOR_VERSION <= 3 - gtk_entry_set_width_chars(GTK_ENTRY(textfield), args->width); -#endif + // An early implementation used gtk_entry_set_width_chars, + // but that is not available on gtk4 and other toolkits + // also don't have this. Setting the width in pixels can + // be implemented on all platforms + gtk_widget_set_size_request(textfield, args->width, -1); } if(frameless) { // TODO: gtk2legacy workaroud @@ -629,8 +641,9 @@ static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool passwor gtk_entry_set_visibility(GTK_ENTRY(textfield), FALSE); } - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, textfield); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, textfield, &layout); if(var) { UiString *value = var->value; @@ -921,8 +934,6 @@ static gboolean ui_path_textfield_key_controller( } UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { - UiObject* current = uic_current_obj(obj); - UiPathTextField *pathtf = malloc(sizeof(UiPathTextField)); memset(pathtf, 0, sizeof(UiPathTextField)); pathtf->obj = obj; @@ -945,8 +956,9 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { pathtf->stack = gtk_stack_new(); gtk_widget_set_name(pathtf->stack, "path-textfield-box"); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, pathtf->stack); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, pathtf->stack, &layout); pathtf->entry_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); pathtf->entry = gtk_entry_new(); @@ -978,7 +990,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); if (var) { UiString* value = (UiString*)var->value; value->obj = pathtf; @@ -1082,8 +1094,6 @@ static GtkWidget* create_path_button_box() { } UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { - UiObject* current = uic_current_obj(obj); - UiPathTextField *pathtf = malloc(sizeof(UiPathTextField)); memset(pathtf, 0, sizeof(UiPathTextField)); pathtf->obj = obj; @@ -1117,8 +1127,9 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { G_CALLBACK(ui_path_textfield_destroy), pathtf); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, eventbox, FALSE); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, eventbox, &layout); // hbox as parent for the GtkEntry and GtkButtonBox GtkWidget *hbox = ui_gtk_hbox_new(0); @@ -1142,7 +1153,7 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) { G_CALLBACK(ui_path_textfield_key_press), pathtf); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_STRING); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); if (var) { UiString* value = (UiString*)var->value; value->obj = pathtf; diff --git a/ui/gtk/toolbar.c b/ui/gtk/toolbar.c index 0409be9..64b221a 100644 --- a/ui/gtk/toolbar.c +++ b/ui/gtk/toolbar.c @@ -128,15 +128,9 @@ static void set_toolbutton_icon(GtkToolItem *item, const char *icon_name) { void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj) { GtkToolItem *button; - if(item->args.stockid) { -#ifdef UI_GTK2 - button = gtk_tool_button_new_from_stock(item->args.stockid); -#else - // TODO: gtk3 stock - button = gtk_tool_button_new(NULL, item->args.label); -#endif - } else { - button = gtk_tool_button_new(NULL, item->args.label); + button = gtk_tool_button_new(NULL, item->args.label); + if(item->args.tooltip) { + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip); } gtk_tool_item_set_homogeneous(button, FALSE); @@ -176,21 +170,16 @@ void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj) { void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObject *obj) { GtkToolItem *button; - if(item->args.stockid) { -#ifdef UI_GTK2 - button = gtk_toggle_tool_button_new_from_stock(item->args.stockid); -#else - button = gtk_toggle_tool_button_new_from_stock(item->args.stockid); // TODO: gtk3 stock -#endif - } else { - button = gtk_toggle_tool_button_new(); - gtk_tool_item_set_homogeneous(button, FALSE); - if(item->args.label) { - gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->args.label); - } - if(item->args.icon) { - set_toolbutton_icon(button, item->args.icon); - } + button = gtk_toggle_tool_button_new(); + gtk_tool_item_set_homogeneous(button, FALSE); + if(item->args.label) { + gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->args.label); + } + if(item->args.icon) { + set_toolbutton_icon(button, item->args.icon); + } + if(item->args.tooltip) { + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip); } ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups); @@ -282,21 +271,15 @@ static void ui_toolbar_menubutton_destroy(GtkWidget *widget, UiToolbarMenuWidget void add_toolitem_menu_widget(GtkToolbar *tb, UiToolbarMenuItem *item, UiObject *obj) { GtkToolItem *button; - if(item->args.stockid) { -#ifdef UI_GTK2 - button = gtk_tool_button_new_from_stock(item->args.stockid); -#else - // TODO: gtk3 stock - button = gtk_tool_button_new(NULL, item->args.label); -#endif - } else { - button = gtk_tool_button_new(NULL, item->args.label); - } + button = gtk_tool_button_new(NULL, item->args.label); gtk_tool_item_set_homogeneous(button, FALSE); if(item->args.icon) { set_toolbutton_icon(button, item->args.icon); } + if(item->args.tooltip) { + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip); + } gtk_tool_item_set_is_important(button, TRUE); gtk_toolbar_insert(tb, button, -1); diff --git a/ui/gtk/toolkit.c b/ui/gtk/toolkit.c index 7a26a70..87d1ef1 100644 --- a/ui/gtk/toolkit.c +++ b/ui/gtk/toolkit.c @@ -40,7 +40,6 @@ #include "../common/toolbar.h" #include "../common/threadpool.h" -#include #include #include @@ -66,6 +65,8 @@ static UiObject *active_window; static int scale_factor = 1; +static UiBool exit_on_shutdown; + UIEXPORT void ui_init(const char *appname, int argc, char **argv) { application_name = appname; uic_init_global_context(); @@ -77,7 +78,6 @@ UIEXPORT void ui_init(const char *appname, int argc, char **argv) { #endif ui_css_init(); - uic_docmgr_init(); uic_menu_init(); uic_toolbar_init(); ui_image_init(); @@ -110,8 +110,11 @@ void ui_onexit(ui_callback f, void *userdata) { exit_data = userdata; } +void ui_app_exit_on_shutdown(UiBool exitapp) { + exit_on_shutdown = exitapp; +} -#ifndef UI_GTK2 +#ifdef UI_APPLICATION static void app_startup(GtkApplication* app, gpointer userdata) { if(startup_func) { startup_func(NULL, startup_data); @@ -119,8 +122,16 @@ static void app_startup(GtkApplication* app, gpointer userdata) { } static void app_activate(GtkApplication* app, gpointer userdata) { - printf("activate\n"); + //printf("activate\n"); +} + +static void app_shutdown(GtkApplication *app, gpointer userdata) { + if(exit_func) { + exit_func(NULL, exit_data); + } + ui_app_save_settings(); } + #endif void ui_main() { @@ -130,6 +141,7 @@ void ui_main() { application_name ? application_name : "application1"); app = UI_APPLICATION_NEW(appid.ptr); g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); + g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_application_run(G_APPLICATION (app), 0, NULL); g_object_unref (app); @@ -140,11 +152,14 @@ void ui_main() { startup_func(NULL, startup_data); } gtk_main(); -#endif if(exit_func) { exit_func(NULL, exit_data); } - uic_store_app_properties(); + ui_app_save_settings(); +#endif + if(exit_on_shutdown) { + exit(0); + } } #ifndef UI_GTK2 @@ -306,15 +321,15 @@ void ui_destroy_widget_var(GtkWidget *object, UiVar *var) { ui_destroy_boundvar(NULL, var); } +// TODO: move to common void ui_destroy_boundvar(UiContext *ctx, UiVar *var) { + uic_save_var(var); uic_unbind_var(var); + // UI_VAR_SPECIAL: anonymous value variable, that is not registered + // in ctx->vars if(var->type == UI_VAR_SPECIAL) { ui_free(var->from_ctx, var); - } else { - ui_free(var->from_ctx, var); - // TODO: free or unbound - //uic_remove_bound_var(ctx, var); } } @@ -378,6 +393,26 @@ static const char *ui_gtk_css = " margin-top: 4px;\n" " margin-bottom: 10px;\n" "}\n" +".ui-listbox-header-row {\n" +" font-weight: bold;\n" +"}\n" +".ui-badge {\n" +" background-color: #e53935;\n" +" color: white;\n" +" border-radius: 9999px;\n" +" padding: 0px 10px 0px 10px;\n" +" font-weight: bold;\n" +" margin-left: 4px;" +" margin-right: 4px;" +"}\n" +".ui-nopadding {" +" padding: 0;" +"}\n" +".ui-table-entry {" +" border: none;" +" box-shadow: none;" +" background: transparent;" +"}\n" ; #elif GTK_MAJOR_VERSION == 3 @@ -411,6 +446,21 @@ static const char *ui_gtk_css = " margin-top: 4px;\n" " margin-bottom: 10px;\n" "}\n" +".ui-listbox-header-row {\n" +" font-weight: bold;\n" +"}\n" +".ui-badge {\n" +" background-color: #e53935;\n" +" color: white;\n" +" border-radius: 9999px;\n" +" padding: 0px 10px 0px 10px;\n" +" font-weight: bold;\n" +" margin-left: 4px;" +" margin-right: 4px;" +"}\n" +".ui-nopadding {" +" padding: 0;" +"}\n" ; #endif @@ -493,3 +543,19 @@ void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, ui_set_enabled(widget, FALSE); } } + +void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const int *states) { + if(!states) { + return; + } + size_t nstates = uic_group_array_size(states); + ui_set_widget_nvisibility_states(ctx, widget, states, nstates); +} + + +void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups) { + if(ngroups > 0) { + uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_visible, states, ngroups); + ui_set_visible(widget, FALSE); + } +} diff --git a/ui/gtk/toolkit.h b/ui/gtk/toolkit.h index b66c695..cfa25bb 100644 --- a/ui/gtk/toolkit.h +++ b/ui/gtk/toolkit.h @@ -74,6 +74,7 @@ extern "C" { #define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon) #define LISTBOX_REMOVE(listbox, row) gtk_list_box_remove(GTK_LIST_BOX(listbox), row) #define LISTBOX_ROW_SET_CHILD(row, child) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), child) +#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), NULL) #define PANED_SET_CHILD1(paned, child) gtk_paned_set_start_child(GTK_PANED(paned), child) #define PANED_SET_CHILD2(paned, child) gtk_paned_set_end_child(GTK_PANED(paned), child) #else @@ -96,6 +97,7 @@ extern "C" { #define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON) #define LISTBOX_REMOVE(listbox, row) gtk_container_remove(GTK_CONTAINER(listbox), row) #define LISTBOX_ROW_SET_CHILD(row, child) gtk_container_add(GTK_CONTAINER(row), child) +#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_container_remove(GTK_CONTAINER(row), gtk_bin_get_child(GTK_BIN(row))) #define PANED_SET_CHILD1(paned, child) gtk_paned_pack1(GTK_PANED(paned), child, TRUE, TRUE) #define PANED_SET_CHILD2(paned, child) gtk_paned_pack2(GTK_PANED(paned), child, TRUE, TRUE) #endif @@ -178,6 +180,8 @@ int ui_get_scalefactor(); void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *style); void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups); void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups); +void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const int *states); +void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups); void ui_destroy_userdata(GtkWidget *object, void *userdata); void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data); diff --git a/ui/gtk/webview.c b/ui/gtk/webview.c index 3b9a946..f8f5718 100644 --- a/ui/gtk/webview.c +++ b/ui/gtk/webview.c @@ -34,13 +34,11 @@ #ifdef UI_WEBVIEW UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) { - UiObject* current = uic_current_obj(obj); - GtkWidget *webview = webkit_web_view_new(); ui_set_name_and_style(webview, args->name, args->style_class); - UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_GENERIC); + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC); if(var) { WebViewData *data = malloc(sizeof(WebViewData)); memset(data, 0, sizeof(WebViewData)); @@ -55,13 +53,14 @@ UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) { value->set = ui_webview_set; value->obj = data; if(value->value && value->type && !strcmp(value->type, UI_WEBVIEW_OBJECT_TYPE)) { - // TODO + ui_webview_set(value, value->value, UI_WEBVIEW_OBJECT_TYPE); } } ui_set_widget_groups(obj->ctx, webview, args->groups); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, webview); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, webview, &layout); return webview; } @@ -97,13 +96,13 @@ int ui_webview_set(UiGeneric *g, void *value, const char *type) { } ui_webview_enable_javascript(g, data->javascript); - webkit_web_view_set_zoom_level(data->webview, data->zoom); + ui_webview_set_zoom(g, data->zoom); return 0; } void ui_webview_load_url(UiGeneric *g, const char *url) { - WebViewData data = { .uri = (char*)url, .type = WEBVIEW_CONTENT_URL }; + WebViewData data = { .uri = (char*)url, .type = WEBVIEW_CONTENT_URL, .javascript = TRUE, .zoom = 1 }; g->set(g, &data, UI_WEBVIEW_OBJECT_TYPE); } @@ -129,6 +128,8 @@ void ui_webview_load_content( data.mimetype = (char*)mimetype; data.encoding = (char*)encoding; data.type = WEBVIEW_CONTENT_CONTENT; + data.javascript = FALSE; + data.zoom = 1; g->set(g, &data, UI_WEBVIEW_OBJECT_TYPE); } diff --git a/ui/gtk/widget.c b/ui/gtk/widget.c index 36f4465..06ef1b2 100644 --- a/ui/gtk/widget.c +++ b/ui/gtk/widget.c @@ -32,22 +32,21 @@ #include "../common/object.h" UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) { - UiObject* current = uic_current_obj(obj); - UIWIDGET widget = create_widget(obj, args, userdata); - UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, widget); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); return widget; } UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) { - UiObject* current = uic_current_obj(obj); GtkWidget *widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); ui_set_name_and_style(widget, args->name, args->style_class); - UI_APPLY_LAYOUT1(current, (*args)); - current->container->add(current->container, widget); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, widget, &layout); return widget; } diff --git a/ui/gtk/window.c b/ui/gtk/window.c index cdafae3..f90997f 100644 --- a/ui/gtk/window.c +++ b/ui/gtk/window.c @@ -49,6 +49,9 @@ static int nwindows = 0; static int window_default_width = 650; static int window_default_height = 550; +static int splitview_window_default_pos = -1; +static UiBool splitview_window_use_prop = TRUE; + static gboolean ui_window_destroy(void *data) { UiObject *obj = data; uic_object_destroy(obj); @@ -77,6 +80,36 @@ void ui_exit_event(GtkWidget *widget, gpointer data) { } static gboolean ui_window_close_request(UiObject *obj) { + if(obj->widget) { + void *appwindow = g_object_get_data(G_OBJECT(obj->widget), "ui.appwindow"); + if(appwindow) { + int width = 0; + int height = 0; +#if GTK_CHECK_VERSION(4, 10, 0) + graphene_rect_t bounds; + if(gtk_widget_compute_bounds(obj->widget, obj->widget, &bounds)) { + width = bounds.size.width; + height = bounds.size.height; + } +#elif GTK_CHECK_VERSION(4, 0, 0) + GtkAllocation alloc; + gtk_widget_get_allocation(GTK_WIDGET(obj->widget), &alloc); + width = alloc.width; + height = alloc.height; +#else + gtk_window_get_size(GTK_WINDOW(obj->widget), &width, &height); +#endif + if(width > 0 && height > 0) { + char width_str[32]; + char height_str[32]; + snprintf(width_str, 32, "%d", width); + snprintf(height_str, 32, "%d", height); + ui_set_property("ui.window.width", width_str); + ui_set_property("ui.window.height", height_str); + } + } + } + uic_context_prepare_close(obj->ctx); obj->ref--; if(obj->ref > 0) { @@ -101,7 +134,14 @@ static gboolean close_request(GtkWidget* self, GdkEvent* event, UiObject *obj) { } #endif -static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool simple) { +static void save_window_splitview_pos(GtkWidget *widget, void *unused) { + int pos = gtk_paned_get_position(GTK_PANED(widget)); + char buf[32]; + snprintf(buf, 32, "%d", pos); + ui_set_property("ui.window.splitview.pos", buf); +} + +static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool splitview, UiBool simple) { UiObject *obj = uic_object_new_toplevel(); #ifdef UI_LIBADWAITA @@ -122,19 +162,28 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side gtk_window_set_title(GTK_WINDOW(obj->widget), title); } - const char *width = ui_get_property("ui.window.width"); - const char *height = ui_get_property("ui.window.height"); - if(width && height) { - gtk_window_set_default_size( - GTK_WINDOW(obj->widget), - atoi(width), - atoi(height)); - } else { - gtk_window_set_default_size( - GTK_WINDOW(obj->widget), - window_default_width + sidebar*250, - window_default_height); + if(!simple) { + g_object_set_data(G_OBJECT(obj->widget), "ui.appwindow", obj); + } + + int window_width = window_default_width; + int window_height = window_default_height; + if(!simple) { + const char *width = ui_get_property("ui.window.width"); + const char *height = ui_get_property("ui.window.height"); + if(width && height) { + int w = atoi(width); + int h = atoi(height); + if(w > 0 && h > 0) { + window_width = w; + window_height = h; + } + } } + gtk_window_set_default_size( + GTK_WINDOW(obj->widget), + window_width, + window_height); obj->destroy = ui_window_widget_destroy; g_signal_connect( @@ -161,50 +210,95 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side GtkWidget *toolbar_view = adw_toolbar_view_new(); adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(toolbar_view), vbox); - GtkWidget *content_box = ui_gtk_vbox_new(0); - BOX_ADD_EXPAND(GTK_BOX(vbox), content_box); + GtkWidget *headerbar_sidebar = NULL; + GtkWidget *headerbar_main = adw_header_bar_new(); + GtkWidget *headerbar_right = NULL; + + GtkWidget *content = toolbar_view; + if(splitview) { + content = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); + g_signal_connect( + content, + "destroy", + G_CALLBACK(save_window_splitview_pos), + NULL); + + const char *splitview_pos_str = ui_get_property("ui.window.splitview.pos"); + int pos = splitview_window_default_pos; + if(pos < 0) { + pos = window_width / 2; + } + if(splitview_pos_str && splitview_window_use_prop) { + int splitview_pos = atoi(splitview_pos_str); + if(splitview_pos > 0) { + pos = splitview_pos; + } + } + gtk_paned_set_position(GTK_PANED(content), pos); + + GtkWidget *right_panel = adw_toolbar_view_new(); + GtkWidget *right_vbox = ui_gtk_vbox_new(0); + adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(right_panel), right_vbox); + + headerbar_right = adw_header_bar_new(); + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_right), FALSE); + adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(right_panel), headerbar_right); + + adw_header_bar_set_show_end_title_buttons(ADW_HEADER_BAR(headerbar_main), FALSE); + + gtk_paned_set_start_child(GTK_PANED(content), toolbar_view); + gtk_paned_set_end_child(GTK_PANED(content), right_panel); + + g_object_set_data(G_OBJECT(obj->widget), "ui_window_splitview", content); + g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel", vbox); + g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel", right_vbox); + } + + GtkWidget *content_box = vbox; - GtkWidget *sidebar_headerbar = NULL; if(sidebar) { GtkWidget *splitview = adw_overlay_split_view_new(); adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), splitview); GtkWidget *sidebar_toolbar_view = adw_toolbar_view_new(); adw_overlay_split_view_set_sidebar(ADW_OVERLAY_SPLIT_VIEW(splitview), sidebar_toolbar_view); - sidebar_headerbar = adw_header_bar_new(); - adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), sidebar_headerbar); + headerbar_sidebar = adw_header_bar_new(); + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE); + adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), headerbar_sidebar); - adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), toolbar_view); + adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), content); g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_toolbar_view); } else { - adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), toolbar_view); + adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), content); } - GtkWidget *headerbar = adw_header_bar_new(); - const char *show_title = ui_get_property("ui.gtk.window.showtitle"); if(show_title) { if(!strcmp(show_title, "main") && sidebar) { - adw_header_bar_set_show_title(ADW_HEADER_BAR(sidebar_headerbar), FALSE); + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE); } else if(!strcmp(show_title, "sidebar")) { - adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar), FALSE); + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE); + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE); } else if(!strcmp(show_title, "false")) { - adw_header_bar_set_show_title(ADW_HEADER_BAR(sidebar_headerbar), FALSE); - adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar), FALSE); + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE); + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE); } else { fprintf(stderr, "Unknown value '%s' for property ui.gtk.window.showtitle\n", show_title); - adw_header_bar_set_show_title(ADW_HEADER_BAR(sidebar_headerbar), FALSE); + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE); } } else { - adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar), FALSE); + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE); + if(sidebar) { + adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE); + } } - adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar); - g_object_set_data(G_OBJECT(obj->widget), "ui_headerbar", headerbar); + adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar_main); + g_object_set_data(G_OBJECT(obj->widget), "ui_headerbar", headerbar_main); if(!simple) { - ui_fill_headerbar(obj, headerbar); + ui_fill_headerbar(obj, headerbar_sidebar, headerbar_main, headerbar_right); } #elif GTK_MAJOR_VERSION >= 4 GtkWidget *content_box = ui_gtk_vbox_new(0); @@ -278,7 +372,8 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side gtk_container_add(GTK_CONTAINER(frame), content_box); obj->container = ui_box_container(obj, content_box); */ - obj->container = ui_box_container(obj, content_box, UI_CONTAINER_VBOX); + UiContainerX *container = ui_box_container(obj, content_box, UI_CONTAINER_VBOX); + uic_object_push_container(obj, container); nwindows++; return obj; @@ -286,15 +381,19 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side UiObject* ui_window(const char *title, void *window_data) { - return create_window(title, window_data, FALSE, FALSE); + return create_window(title, window_data, FALSE, FALSE, FALSE); } UiObject *ui_sidebar_window(const char *title, void *window_data) { - return create_window(title, window_data, TRUE, FALSE); + return create_window(title, window_data, TRUE, FALSE, FALSE); +} + +UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar) { + return create_window(title, NULL, sidebar, TRUE, FALSE); } UiObject* ui_simple_window(const char *title, void *window_data) { - return create_window(title, window_data, FALSE, TRUE); + return create_window(title, window_data, FALSE, FALSE, TRUE); } void ui_window_size(UiObject *obj, int width, int height) { @@ -304,6 +403,54 @@ void ui_window_size(UiObject *obj, int width, int height) { height); } +void ui_window_default_size(int width, int height) { + window_default_width = width; + window_default_height = height; +} + +void ui_splitview_window_set_pos(UiObject *obj, int pos) { + GtkWidget *splitview = g_object_get_data(G_OBJECT(obj->widget), "ui_window_splitview"); + if(splitview) { + gtk_paned_set_position(GTK_PANED(splitview), pos); + } else { + fprintf(stderr, "Error: window has no splitview\n"); + } +} + +int ui_splitview_window_get_pos(UiObject *obj) { + GtkWidget *splitview = g_object_get_data(G_OBJECT(obj->widget), "ui_window_splitview"); + if(splitview) { + return gtk_paned_get_position(GTK_PANED(splitview)); + } else { + fprintf(stderr, "Error: window has no splitview\n"); + } + return 0; +} + +void ui_splitview_window_set_default_pos(int pos) { + splitview_window_default_pos = pos; +} + +void ui_splitview_window_use_property(UiBool enable) { + splitview_window_use_prop = enable; +} + +UIEXPORT void ui_splitview_window_set_visible(UiObject *obj, int pane, UiBool visible) { + GtkWidget *panel = NULL; + if(pane == 0) { + panel = g_object_get_data(G_OBJECT(obj->widget), "ui_left_panel"); + } else if(pane == 1) { + panel = g_object_get_data(G_OBJECT(obj->widget), "ui_right_panel"); + } + + if(panel == NULL) { + fprintf(stderr, "Error: obj is not a splitview window or invalid pane %d specified\n", pane); + return; + } + + gtk_widget_set_visible(panel, visible); +} + #ifdef UI_LIBADWAITA static void dialog_response(AdwAlertDialog *self, gchar *response, UiEventData *data) { @@ -313,7 +460,6 @@ static void dialog_response(AdwAlertDialog *self, gchar *response, UiEventData * evt.window = evt.obj->window; evt.eventdata = NULL; evt.eventdatatype = 0; - evt.eventdatatype = 0; evt.intval = 0; if(!strcmp(response, "btn1")) { @@ -333,35 +479,35 @@ static void dialog_response(AdwAlertDialog *self, gchar *response, UiEventData * } } -void ui_dialog_create(UiObject *parent, UiDialogArgs args) { - AdwDialog *dialog = adw_alert_dialog_new(args.title, args.content); +void ui_dialog_create(UiObject *parent, UiDialogArgs *args) { + AdwDialog *dialog = adw_alert_dialog_new(args->title, args->content); UiEventData *event = malloc(sizeof(UiEventData)); - event->callback = args.result; - event->userdata = args.resultdata; + event->callback = args->result; + event->userdata = args->resultdata; event->customdata = NULL; event->customint = 0; event->value = 0; event->obj = parent; - if(args.button1_label) { - adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn1", args.button1_label); + if(args->button1_label) { + adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn1", args->button1_label); } - if(args.button2_label) { - adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn2", args.button2_label); + if(args->button2_label) { + adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn2", args->button2_label); } - if(args.closebutton_label) { - adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "close", args.closebutton_label); + if(args->closebutton_label) { + adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "close", args->closebutton_label); adw_alert_dialog_set_close_response(ADW_ALERT_DIALOG(dialog), "close"); } GtkWidget *entry = NULL; - if(args.input || args.password) { + if(args->input || args->password) { entry = gtk_entry_new(); - if(args.password) { + if(args->password) { gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); } - if(args.input_value) { - ENTRY_SET_TEXT(entry, args.input_value); + if(args->input_value) { + ENTRY_SET_TEXT(entry, args->input_value); } adw_alert_dialog_set_extra_child(ADW_ALERT_DIALOG(dialog), entry); event->customdata = entry; @@ -410,47 +556,47 @@ static void ui_dialog_response (GtkDialog* self, gint response_id, gpointer user WINDOW_DESTROY(GTK_WIDGET(self)); } -void ui_dialog_create(UiObject *parent, UiDialogArgs args) { +void ui_dialog_create(UiObject *parent, UiDialogArgs *args) { GtkDialog *dialog = GTK_DIALOG(gtk_dialog_new()); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent->widget)); gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); GtkWidget *dialog_w = GTK_WIDGET(dialog); - if(args.title) { - gtk_window_set_title(GTK_WINDOW(dialog), args.title); + if(args->title) { + gtk_window_set_title(GTK_WINDOW(dialog), args->title); } - if(args.button1_label) { - gtk_dialog_add_button(dialog, args.button1_label, 1); + if(args->button1_label) { + gtk_dialog_add_button(dialog, args->button1_label, 1); } - if(args.button2_label) { - gtk_dialog_add_button(dialog, args.button2_label, 2); + if(args->button2_label) { + gtk_dialog_add_button(dialog, args->button2_label, 2); } - if(args.closebutton_label) { - gtk_dialog_add_button(dialog, args.closebutton_label, 0); + if(args->closebutton_label) { + gtk_dialog_add_button(dialog, args->closebutton_label, 0); } GtkWidget *content_area = gtk_dialog_get_content_area(dialog); - if(args.content) { - GtkWidget *label = gtk_label_new(args.content); + if(args->content) { + GtkWidget *label = gtk_label_new(args->content); BOX_ADD(content_area, label); } GtkWidget *textfield = NULL; - if(args.input || args.password) { + if(args->input || args->password) { textfield = gtk_entry_new(); - if(args.password) { + if(args->password) { gtk_entry_set_visibility(GTK_ENTRY(textfield), FALSE); } - if(args.input_value) { - ENTRY_SET_TEXT(textfield, args.input_value); + if(args->input_value) { + ENTRY_SET_TEXT(textfield, args->input_value); } BOX_ADD(content_area, textfield); } UiEventData *event = malloc(sizeof(UiEventData)); event->obj = parent; - event->callback = args.result; - event->userdata = args.resultdata; + event->callback = args->result; + event->userdata = args->resultdata; event->value = 0; event->customdata = textfield; @@ -694,18 +840,6 @@ static void ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action, unsign G_CALLBACK(ui_destroy_userdata), event); - - UiEvent evt; - evt.obj = obj; - evt.document = evt.obj->ctx->document; - evt.window = evt.obj->window; - evt.intval = 0; - - UiFileList flist; - flist.files = NULL; - flist.nfiles = 0; - evt.eventdata = &flist; - gtk_widget_show(dialog); } #endif @@ -749,18 +883,18 @@ static void ui_dialogwindow_response(GtkDialog* self, gint response_id, gpointer -UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { +UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) { GtkWidget *dialog = DIALOG_NEW(); - if(args.width > 0 || args.height > 0) { + if(args->width > 0 || args->height > 0) { gtk_window_set_default_size( GTK_WINDOW(dialog), - args.width, - args.height); + args->width, + args->height); } gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent->widget)); - if(args.modal != UI_OFF) { + if(args->modal != UI_OFF) { gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); } @@ -770,15 +904,15 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { obj->destroy = ui_window_widget_destroy; nwindows++; - if(args.title != NULL) { - gtk_window_set_title(GTK_WINDOW(dialog), args.title); + if(args->title != NULL) { + gtk_window_set_title(GTK_WINDOW(dialog), args->title); } #if ! GTK_CHECK_VERSION(4, 10, 0) UiEventData *event = malloc(sizeof(UiEventData)); event->obj = obj; - event->userdata = args.onclickdata; - event->callback = args.onclick; + event->userdata = args->onclickdata; + event->callback = args->onclick; event->value = 0; event->customdata = NULL; @@ -815,45 +949,46 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { #endif GtkWidget *content_vbox = ui_gtk_vbox_new(0); - obj->container = ui_box_container(obj, content_vbox, UI_CONTAINER_VBOX); - if(args.lbutton1 || args.lbutton2 || args.rbutton3 || args.rbutton4) { + UiContainerX *container = ui_box_container(obj, content_vbox, UI_CONTAINER_VBOX); + uic_object_push_container(obj, container); + if(args->lbutton1 || args->lbutton2 || args->rbutton3 || args->rbutton4) { #if GTK_CHECK_VERSION(3, 10, 0) - if(args.titlebar_buttons != UI_OFF) { + if(args->titlebar_buttons != UI_OFF) { GtkWidget *headerbar = gtk_header_bar_new(); gtk_window_set_titlebar(GTK_WINDOW(dialog), headerbar); - if(args.show_closebutton == UI_OFF) { + if(args->show_closebutton == UI_OFF) { HEADERBAR_SHOW_CLOSEBUTTON(headerbar, FALSE); } - if(args.lbutton1) { - GtkWidget *button = ui_create_button(obj, args.lbutton1, NULL, args.onclick, args.onclickdata, 1, args.default_button == 1); + if(args->lbutton1) { + GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1); gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); - if(args.default_button == 1) { + if(args->default_button == 1) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.lbutton2) { - GtkWidget *button = ui_create_button(obj, args.lbutton2, NULL, args.onclick, args.onclickdata, 2, args.default_button == 2); + if(args->lbutton2) { + GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2); gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); - if(args.default_button == 2) { + if(args->default_button == 2) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.rbutton4) { - GtkWidget *button = ui_create_button(obj, args.rbutton4, NULL, args.onclick, args.onclickdata, 4, args.default_button == 4); + if(args->rbutton4) { + GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4); gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); - if(args.default_button == 4) { + if(args->default_button == 4) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.rbutton3) { - GtkWidget *button = ui_create_button(obj, args.rbutton3, NULL, args.onclick, args.onclickdata, 3, args.default_button == 3); + if(args->rbutton3) { + GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3); gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); - if(args.default_button == 3) { + if(args->default_button == 3) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } @@ -868,21 +1003,21 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); GtkWidget *grid = ui_create_grid_widget(10, 10); - GtkWidget *widget = ui_box_set_margin(grid, 16); + GtkWidget *widget = ui_gtk_set_margin(grid, 16, 0, 0, 0, 0); gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); - if(args.lbutton1) { - GtkWidget *button = ui_create_button(obj, args.lbutton1, NULL, args.onclick, args.onclickdata, 1, args.default_button == 1); + if(args->lbutton1) { + GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1); gtk_grid_attach(GTK_GRID(grid), button, 0, 0, 1, 1); - if(args.default_button == 1) { + if(args->default_button == 1) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.lbutton2) { - GtkWidget *button = ui_create_button(obj, args.lbutton2, NULL, args.onclick, args.onclickdata, 2, args.default_button == 2); + if(args->lbutton2) { + GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2); gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1); - if(args.default_button == 2) { + if(args->default_button == 2) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } @@ -890,18 +1025,18 @@ UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { GtkWidget *space = gtk_label_new(NULL); gtk_widget_set_hexpand(space, TRUE); gtk_grid_attach(GTK_GRID(grid), space, 2, 0, 1, 1); - if(args.rbutton3) { - GtkWidget *button = ui_create_button(obj, args.rbutton3, NULL, args.onclick, args.onclickdata, 3, args.default_button == 3); + if(args->rbutton3) { + GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3); gtk_grid_attach(GTK_GRID(grid), button, 3, 0, 1, 1); - if(args.default_button == 3) { + if(args->default_button == 3) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } } - if(args.rbutton4) { - GtkWidget *button = ui_create_button(obj, args.rbutton4, NULL, args.onclick, args.onclickdata, 4, args.default_button == 4); + if(args->rbutton4) { + GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4); gtk_grid_attach(GTK_GRID(grid), button, 4, 0, 1, 1); - if(args.default_button == 4) { + if(args->default_button == 4) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); DEFAULT_BUTTON(dialog, button); } diff --git a/ui/ui/button.h b/ui/ui/button.h index fb578f7..693f730 100644 --- a/ui/ui/button.h +++ b/ui/ui/button.h @@ -35,6 +35,12 @@ extern "C" { #endif +enum UiLinkType { + UI_LINK_TEXT = 0, + UI_LINK_BUTTON +}; +typedef enum UiLinkType UiLinkType; + typedef struct UiButtonArgs { UiBool fill; UiBool hexpand; @@ -42,19 +48,24 @@ typedef struct UiButtonArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; const char *style_class; - const char* label; - const char* stockid; - const char* icon; + const char *label; + const char *icon; + const char *tooltip; UiLabelType labeltype; ui_callback onclick; - void* onclickdata; + void *onclickdata; - const int* groups; + const int *groups; } UiButtonArgs; typedef struct UiToggleArgs { @@ -64,38 +75,86 @@ typedef struct UiToggleArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; const char *style_class; - const char* label; - const char* stockid; - const char* icon; + const char *label; + const char *icon; + const char *tooltip; UiLabelType labeltype; - UiInteger* value; - const char* varname; + UiInteger *value; + const char *varname; ui_callback onchange; - void* onchangedata; + void *onchangedata; int enable_group; - const int* groups; + const int *groups; } UiToggleArgs; + +typedef struct UiLinkButtonArgs { + UiBool fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + const char *label; + const char *uri; + UiString *value; + const char *varname; + ui_callback onclick; + void *onclickdata; + UiBool nofollow; + UiLinkType type; + + const int *groups; +} UiLinkButtonArgs; #define ui_button(obj, ...) ui_button_create(obj, &(UiButtonArgs){ __VA_ARGS__ } ) #define ui_togglebutton(obj, ...) ui_togglebutton_create(obj, &(UiToggleArgs){ __VA_ARGS__ } ) #define ui_checkbox(obj, ...) ui_checkbox_create(obj, &(UiToggleArgs){ __VA_ARGS__ } ) #define ui_switch(obj, ...) ui_switch_create(obj, &(UiToggleArgs){ __VA_ARGS__ } ) #define ui_radiobutton(obj, ...) ui_radiobutton_create(obj, &(UiToggleArgs){ __VA_ARGS__ } ) +#define ui_linkbutton(obj, ...) ui_linkbutton_create(obj, &(UiLinkButtonArgs){ __VA_ARGS__ }) -UIEXPORT UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args); -UIEXPORT UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args); -UIEXPORT UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args); -UIEXPORT UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args); -UIEXPORT UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args); +UIEXPORT UIWIDGET ui_togglebutton_create(UiObject *obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_checkbox_create(UiObject *obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_switch_create(UiObject *obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args); +UIEXPORT UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args); +UIEXPORT void ui_button_set_label(UIWIDGET button, const char *label); +UIEXPORT void ui_button_set_icon(UIWIDGET button, const char *icon); +UIEXPORT void ui_linkbutton_value_set(UiString *str, const char *label, const char *uri); +UIEXPORT void ui_linkbutton_value_set_label(UiString *str, const char *label); +UIEXPORT void ui_linkbutton_value_set_uri(UiString *str, const char *uri); +UIEXPORT void ui_linkbutton_value_set_visited(UiString *str, UiBool visited); +UIEXPORT void ui_linkbutton_set_label(UIWIDGET button, const char *label); +UIEXPORT void ui_linkbutton_set_uri(UIWIDGET button, const char *label); +UIEXPORT void ui_linkbutton_set_visited(UIWIDGET button, UiBool visited); +UIEXPORT char* ui_linkbutton_get_label(UIWIDGET button); +UIEXPORT char* ui_linkbutton_get_uri(UIWIDGET button); +UIEXPORT UiBool ui_linkbutton_get_visited(UIWIDGET button); #ifdef __cplusplus } diff --git a/ui/ui/container.h b/ui/ui/container.h index 75c78b0..3355850 100644 --- a/ui/ui/container.h +++ b/ui/ui/container.h @@ -65,12 +65,16 @@ typedef struct UiContainerArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; const char *style_class; - int margin; int spacing; int columnspacing; int rowspacing; @@ -87,14 +91,19 @@ typedef struct UiFrameArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; const char *style_class; UiSubContainerType subcontainer; - - int margin; + + int padding; int spacing; int columnspacing; int rowspacing; @@ -110,6 +119,11 @@ typedef struct UiTabViewArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; @@ -124,13 +138,10 @@ typedef struct UiTabViewArgs { UiInteger *value; const char* varname; - int margin; + int padding; int spacing; int columnspacing; int rowspacing; - - const char* label; - UiBool isexpanded; } UiTabViewArgs; typedef struct UiHeaderbarArgs { @@ -140,6 +151,11 @@ typedef struct UiHeaderbarArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; @@ -156,6 +172,10 @@ typedef struct UiSidebarArgs { const char *name; const char *style_class; int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int spacing; } UiSidebarArgs; @@ -166,17 +186,22 @@ typedef struct UiSplitPaneArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; const char *style_class; - int margin; int spacing; int columnspacing; int rowspacing; int initial_position; + const char *position_property; UiInteger *value; const char* varname; int max_panes; @@ -189,12 +214,16 @@ typedef struct UiItemListContainerArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; const char *style_class; - int margin; int spacing; int sub_margin; @@ -227,9 +256,29 @@ typedef struct UiItemListContainerArgs { UiSubContainerType subcontainer; } UiItemListContainerArgs; + +typedef struct UiLayout UiLayout; +struct UiLayout { + UiBool fill; + char *label; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; + int colspan; + int rowspan; +}; + struct UiContainerX { void *container; - int close; + UiBool close; + UiBool newline; UiContainerX *prev; UiContainerX *next; }; @@ -246,6 +295,8 @@ struct UiContainerX { #define ui_tabview(obj, ...) for(ui_tabview_create(obj, &(UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_headerbar(obj, ...) for(ui_headerbar_create(obj, &(UiHeaderbarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_sidebar(obj, ...) for(ui_sidebar_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_left_panel(obj, ...) for(ui_left_panel_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_right_panel(ob, ...) for(ui_right_panel_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_vbox0(obj) for(ui_vbox_create(obj, &(UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_hbox0(obj) for(ui_hbox_create(obj, &(UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) @@ -256,6 +307,9 @@ struct UiContainerX { #define ui_tabview0(obj) for(ui_tabview_create(obj, &(UiTabViewArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_headerbar0(obj) for(ui_headerbar_create(obj, &(UiHeaderbarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_sidebar0(obj) for(ui_sidebar_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_left_panel0(obj) for(ui_left_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_right_panel0(obj) for(ui_right_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) + #define ui_vbox_w(obj, w, ...) for(w = ui_vbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_hbox_w(obj, w, ...) for(w = ui_hbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) @@ -301,6 +355,8 @@ UIEXPORT void ui_headerbar_center_create(UiObject *obj); UIEXPORT void ui_headerbar_end_create(UiObject *obj); UIEXPORT UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args); +UIEXPORT UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args); +UIEXPORT UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args); UIEXPORT UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args); @@ -309,18 +365,6 @@ UIEXPORT UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args); UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBool visible); -// box container layout functions -UIEXPORT void ui_layout_fill(UiObject *obj, UiBool fill); -// grid container layout functions -UIEXPORT void ui_layout_hexpand(UiObject *obj, UiBool expand); -UIEXPORT void ui_layout_vexpand(UiObject *obj, UiBool expand); -UIEXPORT void ui_layout_hfill(UiObject *obj, UiBool fill); -UIEXPORT void ui_layout_vfill(UiObject *obj, UiBool fill); -UIEXPORT void ui_layout_override_defaults(UiObject *obj, UiBool d); -UIEXPORT void ui_layout_width(UiObject *obj, int width); -UIEXPORT void ui_layout_height(UiObject* obj, int width); -UIEXPORT void ui_layout_colspan(UiObject *obj, int cols); -UIEXPORT void ui_layout_rowspan(UiObject* obj, int rows); UIEXPORT void ui_newline(UiObject *obj); // TODO @@ -334,7 +378,7 @@ UIEXPORT void ui_container_begin_close(UiObject *obj); UIEXPORT int ui_container_finish(UiObject *obj); #define UI_APPLY_LAYOUT1(obj, args) \ - if(args.fill != UI_DEFAULT) ui_layout_fill(obj, args.fill == UI_ON ? 1 : 0 ); \ + if(args.fill) ui_layout_fill(obj, 1); \ if(args.hexpand) ui_layout_hexpand(obj, 1); \ if(args.vexpand) ui_layout_vexpand(obj, 1); \ if(args.hfill) ui_layout_hfill(obj, 1); \ @@ -345,7 +389,7 @@ UIEXPORT int ui_container_finish(UiObject *obj); /*force caller to add ';'*/(void)0 #define UI_APPLY_LAYOUT2(obj, args) \ - if(args->fill != UI_DEFAULT) ui_layout_fill(obj, args->fill == UI_ON ? 1 : 0 ); \ + if(args->fill) ui_layout_fill(obj, 1); \ if(args->hexpand) ui_layout_hexpand(obj, 1); \ if(args->vexpand) ui_layout_vexpand(obj, 1); \ if(args->hfill) ui_layout_hfill(obj, 1); \ @@ -355,7 +399,21 @@ UIEXPORT int ui_container_finish(UiObject *obj); if(args->rowspan > 0) ui_layout_rowspan(obj, args->rowspan); \ /*force caller to add ';'*/(void)0 - +#define UI_ARGS2LAYOUT(args) { \ + .fill = args->fill, \ + .hexpand = args->hexpand, \ + .vexpand = args->vexpand, \ + .hfill = args->hfill, \ + .vfill = args->vfill, \ + .override_defaults = args->override_defaults, \ + .margin = args->margin, \ + .margin_left = args->margin_left, \ + .margin_right = args->margin_right, \ + .margin_top = args->margin_top, \ + .margin_bottom = args->margin_bottom, \ + .colspan = args->colspan, \ + .rowspan = args->rowspan } + #ifdef __cplusplus } #endif diff --git a/ui/ui/display.h b/ui/ui/display.h index 3cb3d65..1ed1235 100644 --- a/ui/ui/display.h +++ b/ui/ui/display.h @@ -38,15 +38,7 @@ #ifdef __cplusplus extern "C" { #endif - -enum UiAlignment { - UI_ALIGN_DEFAULT = 0, - UI_ALIGN_LEFT, - UI_ALIGN_RIGHT, - UI_ALIGN_CENTER -}; - -typedef enum UiAlignment UiAlignment; + enum UiLabelStyle { UI_LABEL_STYLE_DEFAULT = 0, @@ -64,6 +56,11 @@ typedef struct UiLabelArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; @@ -83,6 +80,11 @@ typedef struct UiProgressbarArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; int width; @@ -102,6 +104,11 @@ typedef struct UiProgressbarSpinnerArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; diff --git a/ui/ui/entry.h b/ui/ui/entry.h index cb1b7f7..6dd6a0a 100644 --- a/ui/ui/entry.h +++ b/ui/ui/entry.h @@ -36,20 +36,28 @@ extern "C" { #endif -typedef struct UiSpinnerArgs { +typedef struct UiSpinBoxArgs { UiBool fill; UiBool hexpand; UiBool vexpand; UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; + int width; const char *name; const char *style_class; double step; int digits; + double min; + double max; UiInteger *intvalue; UiDouble* doublevalue; UiRange *rangevalue; @@ -58,13 +66,13 @@ typedef struct UiSpinnerArgs { void* onchangedata; const int *groups; -} UiSpinnerArgs; +} UiSpinBoxArgs; -UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args); +UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args); -#define ui_spinner(obj, ...) ui_spinner_create(obj, &(UiSpinnerArgs){ __VA_ARGS__ } ) +#define ui_spinbox(obj, ...) ui_spinbox_create(obj, &(UiSpinBoxArgs){ __VA_ARGS__ } ) void ui_spinner_setrange(UIWIDGET spinner, double min, double max); void ui_spinner_setdigits(UIWIDGET spinner, int digits); diff --git a/ui/ui/graphics.h b/ui/ui/graphics.h index 6eb0daf..e0063b9 100644 --- a/ui/ui/graphics.h +++ b/ui/ui/graphics.h @@ -45,25 +45,53 @@ struct UiGraphics { int height; }; -UIWIDGET ui_drawingarea(UiObject *obj, ui_drawfunc f, void *userdata); -void ui_drawingarea_mousehandler(UiObject *obj, UIWIDGET widget, ui_callback f, void *u); -void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height); -void ui_drawingarea_redraw(UIWIDGET drawingarea); +typedef struct UiDrawingAreaArgs { + UiBool fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + int width; + int height; + ui_drawfunc draw; + void *drawdata; + ui_callback onclick; + void *onclickdata; + ui_callback onmotion; + void *onmotiondata; +} UiDrawingAreaArgs; + +#define ui_drawingarea(obj, ...) ui_drawingarea_create(obj, &(UiDrawingAreaArgs) { __VA_ARGS__ } ) + +UIEXPORT UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args); +UIEXPORT void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height); +UIEXPORT void ui_drawingarea_redraw(UIWIDGET drawingarea); // text layout -UiTextLayout* ui_text(UiGraphics *g); -void ui_text_free(UiTextLayout *text); -void ui_text_setstring(UiTextLayout *layout, char *str); -void ui_text_setstringl(UiTextLayout *layout, char *str, int len); -void ui_text_setfont(UiTextLayout *layout, char *font, int size); -void ui_text_getsize(UiTextLayout *layout, int *width, int *height); -void ui_text_setwidth(UiTextLayout *layout, int width); +UIEXPORT UiTextLayout* ui_text(UiGraphics *g); +UIEXPORT void ui_text_free(UiTextLayout *text); +UIEXPORT void ui_text_setstring(UiTextLayout *layout, char *str); +UIEXPORT void ui_text_setstringl(UiTextLayout *layout, char *str, int len); +UIEXPORT void ui_text_setfont(UiTextLayout *layout, const char *font, int size); +UIEXPORT void ui_text_getsize(UiTextLayout *layout, int *width, int *height); +UIEXPORT void ui_text_setwidth(UiTextLayout *layout, int width); // drawing functions -void ui_graphics_color(UiGraphics *g, int red, int green, int blue); -void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2); -void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, int fill); -void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text); +UIEXPORT void ui_graphics_color(UiGraphics *g, int red, int green, int blue); +UIEXPORT void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2); +UIEXPORT void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, UiBool fill); +UIEXPORT void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text); #ifdef __cplusplus } diff --git a/ui/ui/image.h b/ui/ui/image.h index 405b509..77d923f 100644 --- a/ui/ui/image.h +++ b/ui/ui/image.h @@ -51,6 +51,11 @@ typedef struct UiImageViewerArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; diff --git a/ui/ui/menu.h b/ui/ui/menu.h index 3f805b8..9dde9b4 100644 --- a/ui/ui/menu.h +++ b/ui/ui/menu.h @@ -38,7 +38,6 @@ extern "C" { typedef struct UiMenuItemArgs { const char* label; - const char* stockid; const char* icon; ui_callback onclick; @@ -49,7 +48,6 @@ typedef struct UiMenuItemArgs { typedef struct UiMenuToggleItemArgs { const char* label; - const char* stockid; const char* icon; const char* varname; @@ -96,7 +94,8 @@ UIEXPORT void ui_menu_end(void); // TODO: private #define ui_contextmenu(builder) for(ui_contextmenu_builder(builder);ui_menu_is_open();ui_menu_close()) UIEXPORT void ui_contextmenu_builder(UiMenuBuilder **out_builder); -UIEXPORT void ui_menubuilder_free(UiMenuBuilder *builder); +UIEXPORT void ui_menubuilder_ref(UiMenuBuilder *builder); +UIEXPORT void ui_menubuilder_unref(UiMenuBuilder *builder); UIEXPORT UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget); UIEXPORT void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y); diff --git a/ui/ui/properties.h b/ui/ui/properties.h index 5c985d9..ffb10dd 100644 --- a/ui/ui/properties.h +++ b/ui/ui/properties.h @@ -41,14 +41,14 @@ const char* ui_set_default_property(const char *name, const char *value); int ui_properties_store(void); -void ui_locales_dir(char *path); -void ui_pixmaps_dir(char *path); +void ui_locales_dir(const char *path); +void ui_pixmaps_dir(const char *path); -void ui_load_lang(char *locale); +void ui_load_lang(const char *locale); void ui_load_lang_def(char *locale, char *default_locale); -char* uistr(char *name); -char* uistr_n(char *name); +char* uistr(const char *name); +char* uistr_n(const char *name); #ifdef __cplusplus } diff --git a/ui/ui/text.h b/ui/ui/text.h index 0daf925..ac5a2c1 100644 --- a/ui/ui/text.h +++ b/ui/ui/text.h @@ -42,9 +42,15 @@ typedef struct UiTextAreaArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; int width; + int height; const char *name; const char *style_class; @@ -63,6 +69,11 @@ typedef struct UiTextFieldArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; int width; @@ -97,6 +108,11 @@ typedef struct UiPathTextFieldArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; diff --git a/ui/ui/toolbar.h b/ui/ui/toolbar.h index 4b42af2..26a79e6 100644 --- a/ui/ui/toolbar.h +++ b/ui/ui/toolbar.h @@ -37,38 +37,46 @@ extern "C" { #endif typedef struct UiToolbarItemArgs { - const char* label; - const char* stockid; - const char* icon; - - ui_callback onclick; - void* onclickdata; - - const int *groups; + const char *label; + const char *icon; + const char *tooltip; + + ui_callback onclick; + void* onclickdata; + + const int *groups; + const int *visibility_states; } UiToolbarItemArgs; typedef struct UiToolbarToggleItemArgs { - const char* label; - const char* stockid; - const char* icon; - - const char* varname; - ui_callback onchange; - void* onchangedata; - - const int *groups; + const char *label; + const char *icon; + const char *tooltip; + + const char *varname; + ui_callback onchange; + void *onchangedata; + + const int *groups; + const int *visibility_states; } UiToolbarToggleItemArgs; typedef struct UiToolbarMenuArgs { - const char* label; - const char* stockid; - const char* icon; + const char *label; + const char *icon; + const char *tooltip; + const int *visibility_states; } UiToolbarMenuArgs; enum UiToolbarPos { - UI_TOOLBAR_LEFT = 0, - UI_TOOLBAR_CENTER, - UI_TOOLBAR_RIGHT + UI_TOOLBAR_LEFT = 0, + UI_TOOLBAR_CENTER, + UI_TOOLBAR_RIGHT, + UI_TOOLBAR_SIDEBAR_LEFT, + UI_TOOLBAR_SIDEBAR_RIGHT, + UI_TOOLBAR_RIGHTPANEL_LEFT, + UI_TOOLBAR_RIGHTPANEL_CENTER, + UI_TOOLBAR_RIGHTPANEL_RIGHT }; #define ui_toolbar_item(name, ...) ui_toolbar_item_create(name, &(UiToolbarItemArgs){ __VA_ARGS__ } ) diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index 7b54742..83cd78a 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -88,13 +88,7 @@ typedef void* UIMENU; // NSMenu* #elif UI_WIN32 -#include - -#define UIEXPORT __declspec(dllexport) - -typedef struct W32Widget { - HWND hwnd; -} W32Widget; +#include "win32.h" #define UIWIDGET W32Widget* #define UIWINDOW void* @@ -200,6 +194,9 @@ typedef struct UiFileList UiFileList; typedef struct UiListSelection UiListSelection; +typedef struct UiTextStyle UiTextStyle; +typedef struct UiColor UiColor; + /* begin opaque types */ typedef struct UiContext UiContext; typedef struct UiContainer UiContainer; @@ -237,11 +234,21 @@ typedef enum UiDnDAction { UI_DND_ACTION_LINK, UI_DND_ACTION_CUSTOM } UiDnDAction; - + +enum UiAlignment { + UI_ALIGN_DEFAULT = 0, + UI_ALIGN_LEFT, + UI_ALIGN_RIGHT, + UI_ALIGN_CENTER +}; + +typedef enum UiAlignment UiAlignment; + typedef void(*ui_callback)(UiEvent*, void*); /* event, user data */ typedef void*(*ui_getvaluefunc)(void *elm, int col); typedef void*(*ui_getvaluefunc2)(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult); +typedef UiBool(*ui_getstylefunc)(UiList *list, void *elm, int row, int col, void *userdata, UiTextStyle *style); typedef int(*ui_threadfunc)(void*); @@ -249,6 +256,9 @@ typedef void(*ui_freefunc)(void*); typedef void(*ui_enablefunc)(void*, int); +typedef void (*ui_destructor_func)(void *memory); + + struct UiObject { /* * native widget @@ -272,11 +282,6 @@ struct UiObject { */ UiContext *ctx; - /* - * container interface (deprecated) - */ - UiContainer *container; - /* * container list * TODO: remove old UiContainer and rename UiContainerX to UiContainer @@ -403,6 +408,7 @@ struct UiGeneric { void* (*get)(UiGeneric*); const char* (*get_type)(UiGeneric*); int (*set)(UiGeneric*, void *, const char *type); + void (*destroy)(UiGeneric*); void *obj; void *value; @@ -493,6 +499,23 @@ enum UiEventType { UI_EVENT_DATA_FILE_LIST }; +#define UI_COLOR(r, g, b) (UiColor){r, g, b} +struct UiColor { + uint8_t red; + uint8_t green; + uint8_t blue; +}; + +#define UI_TEXT_STYLE_BOLD 1 +#define UI_TEXT_STYLE_ITALIC 2 +#define UI_TEXT_STYLE_UNDERLINE 4 + +struct UiTextStyle { + uint32_t text_style; + UiColor fg; + UiBool fg_set; +}; + UIEXPORT void ui_init(const char *appname, int argc, char **argv); UIEXPORT const char* ui_appname(); @@ -514,6 +537,9 @@ UIEXPORT void ui_onstartup(ui_callback f, void *userdata); UIEXPORT void ui_onopen(ui_callback f, void *userdata); UIEXPORT void ui_onexit(ui_callback f, void *userdata); +UIEXPORT int ui_app_save_settings(void); +UIEXPORT void ui_app_exit_on_shutdown(UiBool exitapp); + UIEXPORT void ui_main(void); UIEXPORT void ui_show(UiObject *obj); UIEXPORT void ui_close(UiObject *obj); @@ -535,6 +561,8 @@ UIEXPORT void ui_attach_document(UiContext *ctx, void *document); UIEXPORT void ui_detach_document(UiContext *ctx, void *document); UIEXPORT void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...); +UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups); +UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates); UIEXPORT void ui_set_group(UiContext *ctx, int group); UIEXPORT void ui_unset_group(UiContext *ctx, int group); @@ -548,6 +576,8 @@ UIEXPORT void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize); UIEXPORT void ui_free(UiContext *ctx, void *ptr); UIEXPORT void* ui_realloc(UiContext *ctx, void *ptr, size_t size); UIEXPORT char* ui_strdup(UiContext *ctx, const char *str); +UIEXPORT void ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr); +UIEXPORT void ui_set_destructor(void *mem, ui_destructor_func destr); // types @@ -578,6 +608,13 @@ UIEXPORT void ui_string_set(UiString *s, const char *value); UIEXPORT char* ui_string_get(UiString *s); UIEXPORT void ui_text_set(UiText *s, const char* value); UIEXPORT char* ui_text_get(UiText *s); +UIEXPORT void ui_range_set(UiRange *r, double value); +UIEXPORT void ui_range_set_range(UiRange *r, double min, double max); +UIEXPORT void ui_range_set_extent(UiRange *r, double extent); +UIEXPORT double ui_range_get(UiRange *r); +UIEXPORT double ui_range_get_min(UiRange *r); +UIEXPORT double ui_range_get_max(UiRange *r); +UIEXPORT double ui_range_get_extent(UiRange *r); UIEXPORT void ui_generic_set_image(UiGeneric *g, void *img); UIEXPORT void* ui_generic_get_image(UiGeneric *g); @@ -608,6 +645,8 @@ UIEXPORT void ui_list_prepend(UiList *list, void *data); UIEXPORT void ui_list_remove(UiList *list, int i); UIEXPORT void ui_list_clear(UiList *list); UIEXPORT void ui_list_update(UiList *list); +UIEXPORT void ui_list_update_row(UiList *list, int row); +UIEXPORT UiListSelection ui_list_get_selection(UiList *list); UIEXPORT void ui_list_addobsv(UiList *list, ui_callback f, void *data); UIEXPORT void ui_list_notify(UiList *list); @@ -622,11 +661,6 @@ UIEXPORT char* ui_clipboard_get(); UIEXPORT void ui_add_image(char *imgname, char *filename); // TODO: remove? -// general widget functions -UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled); -UIEXPORT void ui_set_show_all(UIWIDGET widget, int value); -UIEXPORT void ui_set_visible(UIWIDGET widget, int visible); - UIEXPORT void ui_listselection_free(UiListSelection selection); @@ -637,7 +671,7 @@ UIEXPORT UiStr ui_str_free(char *str, void (*free)(void *v)); UIEXPORT char* ui_getappdir(void); -UIEXPORT char* ui_configfile(char *name); +UIEXPORT char* ui_configfile(const char *name); UIEXPORT UiCondVar* ui_condvar_create(void); UIEXPORT void ui_condvar_wait(UiCondVar *var); diff --git a/ui/ui/tree.h b/ui/ui/tree.h index af51d08..33a903c 100644 --- a/ui/ui/tree.h +++ b/ui/ui/tree.h @@ -51,15 +51,33 @@ typedef enum UiModelType { UI_INTEGER, UI_ICON, UI_ICON_TEXT, - UI_ICON_TEXT_FREE + UI_ICON_TEXT_FREE, + UI_STRING_EDITABLE, + UI_BOOL_EDITABLE } UiModelType; +typedef struct UiCellValue { + union { + const char *string; + int64_t i; + UiBool b; + }; + UiModelType type; +} UiCellValue; + +typedef UiBool (*ui_list_savefunc)(UiList *list, int row, int col, UiCellValue *value, void *userdata); + struct UiModel { /* * number of columns */ int columns; + /* + * current allocation size (internal) + */ + int alloc; + /* * array of column types * array length is the number of columns @@ -76,25 +94,6 @@ struct UiModel { * array of column size hints */ int *columnsize; - - /* - * void*(*ui_getvaluefunc)(void *elm, int col); - * - * function for translating model data to view data - * first argument is the pointer returned by UiList first|next|get - * second argument is the column index - * TODO: return - */ - ui_getvaluefunc getvalue; - - /* - * void*(*ui_getvaluefunc2)(UiList *list, void *elm, int row, int col, void *userdata) - * - * alternative for getvalue - */ - ui_getvaluefunc2 getvalue2; - - void *getvalue2data; }; struct UiListCallbacks { @@ -121,11 +120,18 @@ struct UiListArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; + int width; + int height; + const char *name; const char *style_class; - UiList* list; const char* varname; UiModel* model; @@ -134,6 +140,8 @@ struct UiListArgs { ui_getvaluefunc getvalue; ui_getvaluefunc2 getvalue2; void *getvalue2data; + ui_getstylefunc getstyle; + void *getstyledata; ui_callback onactivate; void* onactivatedata; ui_callback onselection; @@ -146,6 +154,8 @@ struct UiListArgs { void* ondropdata; UiBool multiselection; UiMenuBuilder *contextmenu; + ui_list_savefunc onsave; + void *onsavedata; const int *groups; }; @@ -175,12 +185,13 @@ typedef struct UiSubListEventData { * will be passed to free */ struct UiSubListItem { - char *icon; - char *label; - char *button_icon; - char *button_label; - char *badge; - void *eventdata; + char *icon; + char *label; + char *button_icon; + char *button_label; + UiMenuBuilder *button_menu; + char *badge; + void *eventdata; }; struct UiSourceListArgs { @@ -190,8 +201,15 @@ struct UiSourceListArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; + int width; + int height; const char *name; const char *style_class; @@ -237,6 +255,11 @@ struct UiSourceListArgs { */ void *getvaluedata; + /* + * is a sublist header a selectable item + */ + UiBool header_is_item; + /* * activated when a list item is selected */ @@ -248,13 +271,26 @@ struct UiSourceListArgs { */ ui_callback onbuttonclick; void *onbuttonclickdata; + + UiMenuBuilder *contextmenu; }; #define UI_SUBLIST(...) (UiSubList){ __VA_ARGS__ } #define UI_SUBLISTS(...) (UiSubList[]){ __VA_ARGS__, (UiSubList){NULL,NULL,NULL,0} } +/* + * Creates an UiModel, that specifies columns for a table widget. + * + * For each column a column type (UiModelType) and a title string + * (char*) must be specified. The column list must be terminated + * with -1. + * + * UiModel *model = ui_model(ctx, UI_STRING, "Column 1", UI_STRING, "Column 2", -1); + */ UIEXPORT UiModel* ui_model(UiContext *ctx, ...); +UIEXPORT UiModel* ui_model_new(UiContext *ctx); +UIEXPORT void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width); UIEXPORT UiModel* ui_model_copy(UiContext *ctx, UiModel* model); UIEXPORT void ui_model_free(UiContext *ctx, UiModel *mi); @@ -278,10 +314,23 @@ UIEXPORT void ui_sublist_item_set_icon(UiSubListItem *item, const char *icon); UIEXPORT void ui_sublist_item_set_label(UiSubListItem *item, const char *label); UIEXPORT void ui_sublist_item_set_button_icon(UiSubListItem *item, const char *button_icon); UIEXPORT void ui_sublist_item_set_button_label(UiSubListItem *item, const char *button_label); +UIEXPORT void ui_sublist_item_set_button_menu(UiSubListItem *item, UiMenuBuilder *menu); UIEXPORT void ui_sublist_item_set_badge(UiSubListItem *item, const char *badge); UIEXPORT void ui_sublist_item_set_eventdata(UiSubListItem *item, void *eventdata); + +/* + * Only relevant for some language bindings + */ +typedef void(*ui_sourcelist_update_func)(void); + +/* + * The sourcelist update callback is called after any source list + * sublist update is completed + */ +UIEXPORT void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb); + #ifdef __cplusplus } #endif diff --git a/ui/ui/webview.h b/ui/ui/webview.h index 36f77f9..9636165 100644 --- a/ui/ui/webview.h +++ b/ui/ui/webview.h @@ -36,15 +36,28 @@ extern "C" { #endif +/* + * WebView type string used by UiGeneric + */ #define UI_WEBVIEW_OBJECT_TYPE "webview" - + +/* + * UiWebViewData* is returned by a webviews UiGeneric->get function + */ +typedef struct UiWebViewData UiWebViewData; + typedef struct UiWebviewArgs { - UiTri fill; + UiBool fill; UiBool hexpand; UiBool vexpand; UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; @@ -58,11 +71,11 @@ typedef struct UiWebviewArgs { #define ui_webview(obj, ...) ui_webview_create(obj, &(UiWebviewArgs){ __VA_ARGS__ } ) -UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args); +UIEXPORT UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args); -void ui_webview_load_url(UiGeneric *g, const char *url); +UIEXPORT void ui_webview_load_url(UiGeneric *g, const char *url); -void ui_webview_load_content( +UIEXPORT void ui_webview_load_content( UiGeneric *g, const char *uri, const char *content, @@ -70,16 +83,20 @@ void ui_webview_load_content( const char *mimetype, const char *encoding); +/* + * Frees a UiWebViewData object returned by a webviews UiGeneric->get function + */ +UIEXPORT void ui_webview_data_free(UiWebViewData *data); -void ui_webview_reload(UiGeneric *g); -UiBool ui_webview_can_go_back(UiGeneric *g); -UiBool ui_webview_can_go_forward(UiGeneric *g); -void ui_webview_go_back(UiGeneric *g); -void ui_webview_go_forward(UiGeneric *g); -const char * ui_webview_get_uri(UiGeneric *g); -void ui_webview_enable_javascript(UiGeneric *g, UiBool enable); -void ui_webview_set_zoom(UiGeneric *g, double zoom); -double ui_webview_get_zoom(UiGeneric *g); +UIEXPORT void ui_webview_reload(UiGeneric *g); +UIEXPORT UiBool ui_webview_can_go_back(UiGeneric *g); +UIEXPORT UiBool ui_webview_can_go_forward(UiGeneric *g); +UIEXPORT void ui_webview_go_back(UiGeneric *g); +UIEXPORT void ui_webview_go_forward(UiGeneric *g); +UIEXPORT const char * ui_webview_get_uri(UiGeneric *g); +UIEXPORT void ui_webview_enable_javascript(UiGeneric *g, UiBool enable); +UIEXPORT void ui_webview_set_zoom(UiGeneric *g, double zoom); +UIEXPORT double ui_webview_get_zoom(UiGeneric *g); #ifdef __cplusplus diff --git a/ui/ui/widget.h b/ui/ui/widget.h index fee08d9..aea5b8e 100644 --- a/ui/ui/widget.h +++ b/ui/ui/widget.h @@ -41,6 +41,11 @@ typedef struct UiWidgetArgs { UiBool hfill; UiBool vfill; UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; int colspan; int rowspan; const char *name; @@ -69,6 +74,9 @@ UIEXPORT UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args); #define ui_separator(obj, ...) ui_separator_create(obj, &(UiWidgetArgs){ __VA_ARGS__ } ) +UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled); +UIEXPORT void ui_set_visible(UIWIDGET widget, int visible); + UIEXPORT void ui_widget_set_size(UIWIDGET w, int width, int height); UIEXPORT void ui_widget_redraw(UIWIDGET w); diff --git a/ui/ui/win32.h b/ui/ui/win32.h new file mode 100644 index 0000000..54d0543 --- /dev/null +++ b/ui/ui/win32.h @@ -0,0 +1,71 @@ +/* + * 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 UI_WIN32_H +#define UI_WIN32_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define UIEXPORT __declspec(dllexport) + +typedef struct W32WidgetClass W32WidgetClass; +typedef struct W32Widget W32Widget; +typedef struct W32Size W32Size; + +typedef void (*W32LayoutFunc)(void *, int, int); + +struct W32Size { + int width; + int height; +}; + +struct W32WidgetClass { + void (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + void (*show)(W32Widget *widget, BOOLEAN show); + void (*enable)(W32Widget *widget, BOOLEAN enable); + W32Size (*get_preferred_size)(W32Widget *widget); + void (*destroy)(W32Widget *widget); +}; + +struct W32Widget { + W32WidgetClass *wclass; + HWND hwnd; + void *userdata; + void (*layout)(void *layout, int width, int height); + void *layoutmanager; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* UI_WIN32_H */ diff --git a/ui/ui/window.h b/ui/ui/window.h index 352abb7..83e85a3 100644 --- a/ui/ui/window.h +++ b/ui/ui/window.h @@ -74,17 +74,25 @@ typedef struct UiDialogWindowArgs { UIEXPORT UiObject *ui_window(const char *title, void *window_data); UIEXPORT UiObject *ui_sidebar_window(const char *title, void *window_data); +UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar); UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data); -UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args); +UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args); -#define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, (UiDialogWindowArgs){ __VA_ARGS__ }); -#define ui_dialog_window0(parent) ui_dialog_window_create(parent, (UiDialogWindowArgs){ 0 }); +#define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ __VA_ARGS__ }); +#define ui_dialog_window0(parent) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ 0 }); UIEXPORT void ui_window_size(UiObject *obj, int width, int height); +UIEXPORT void ui_window_default_size(int width, int height); -#define ui_dialog(parent, ...) ui_dialog_create(parent, (UiDialogArgs){ __VA_ARGS__ } ) +UIEXPORT void ui_splitview_window_set_pos(UiObject *obj, int pos); +UIEXPORT int ui_splitview_window_get_pos(UiObject *obj); +UIEXPORT void ui_splitview_window_set_default_pos(int pos); +UIEXPORT void ui_splitview_window_use_property(UiBool enable); +UIEXPORT void ui_splitview_window_set_visible(UiObject *obj, int pane, UiBool visible); -UIEXPORT void ui_dialog_create(UiObject *parent, UiDialogArgs args); +#define ui_dialog(parent, ...) ui_dialog_create(parent, &(UiDialogArgs){ __VA_ARGS__ } ) + +UIEXPORT void ui_dialog_create(UiObject *parent, UiDialogArgs *args); UIEXPORT void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata); UIEXPORT void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata);