From 23cf70419519e8b63cab8da5b3271be9abe9645a Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Mon, 26 May 2025 21:31:49 +0200 Subject: [PATCH] update uwproj, ucx and toolkit --- configure | 2 + make/project.xml | 4 +- ucx/README | 4 - ucx/array.c | 467 ----------------------------- ucx/avl.c | 373 ----------------------- ucx/logging.c | 117 -------- ucx/stack.c | 165 ---------- ucx/test.c | 91 ------ ucx/utils.c | 448 ---------------------------- ui/Makefile | 4 +- ui/common/condvar.c | 4 + ui/common/context.c | 87 +++--- ui/common/context.h | 27 +- ui/common/document.c | 2 +- ui/common/objs.mk | 26 +- ui/common/properties.c | 6 +- ui/common/threadpool.c | 4 +- ui/common/types.c | 30 +- ui/gtk/button.c | 9 +- ui/gtk/container.c | 49 +-- ui/gtk/container.h | 15 +- ui/gtk/image.c | 434 ++++++++++++++++++++++++--- ui/gtk/image.h | 88 ++++++ ui/gtk/list.c | 368 +++++++++++++++++------ ui/gtk/list.h | 7 +- ui/gtk/menu.c | 117 ++++++-- ui/gtk/menu.h | 14 +- ui/gtk/model.c | 539 --------------------------------- ui/gtk/model.h | 149 ---------- ui/gtk/objs.mk | 1 + ui/gtk/text.c | 163 ++++++---- ui/gtk/text.h | 3 + ui/gtk/toolkit.c | 30 +- ui/gtk/toolkit.h | 4 + ui/gtk/tree.c | 562 ----------------------------------- ui/gtk/tree.h | 88 ------ ui/gtk/webview.h | 5 + ui/gtk/widget.c | 60 ++++ ucx/ucx.c => ui/gtk/widget.h | 56 ++-- ui/ui/container.h | 31 +- ui/ui/display.h | 1 - ui/ui/image.h | 28 ++ ui/ui/toolkit.h | 82 +++-- ui/ui/tree.h | 5 + ui/ui/ui.h | 1 + ui/ui/widget.h | 81 +++++ 46 files changed, 1453 insertions(+), 3398 deletions(-) delete mode 100644 ucx/README delete mode 100644 ucx/array.c delete mode 100644 ucx/avl.c delete mode 100644 ucx/logging.c delete mode 100644 ucx/stack.c delete mode 100644 ucx/test.c delete mode 100644 ucx/utils.c delete mode 100644 ui/gtk/model.c delete mode 100644 ui/gtk/model.h delete mode 100644 ui/gtk/tree.c delete mode 100644 ui/gtk/tree.h create mode 100644 ui/gtk/widget.c rename ucx/ucx.c => ui/gtk/widget.h (59%) create mode 100644 ui/ui/widget.h diff --git a/configure b/configure index c8bd057..3f1227f 100755 --- a/configure +++ b/configure @@ -676,6 +676,7 @@ do cat >> "$TEMP_DIR/make.mk" << __EOF__ OBJ_EXT = .o LIB_EXT = .a +LIB_PREFIX = lib PACKAGE_SCRIPT = package_osx.sh __EOF__ break @@ -696,6 +697,7 @@ do cat >> "$TEMP_DIR/make.mk" << __EOF__ OBJ_EXT = .o LIB_EXT = .a +LIB_PREFIX = lib PACKAGE_SCRIPT = package_unix.sh __EOF__ break diff --git a/make/project.xml b/make/project.xml index 21adfa3..b2553d3 100644 --- a/make/project.xml +++ b/make/project.xml @@ -1,5 +1,5 @@ - + c @@ -100,11 +100,13 @@ OBJ_EXT = .o LIB_EXT = .a + LIB_PREFIX = lib PACKAGE_SCRIPT = package_osx.sh OBJ_EXT = .o LIB_EXT = .a + LIB_PREFIX = lib PACKAGE_SCRIPT = package_unix.sh diff --git a/ucx/README b/ucx/README deleted file mode 100644 index d0890d0..0000000 --- a/ucx/README +++ /dev/null @@ -1,4 +0,0 @@ -UCX is a library for common data structures, algorithms and string functions. - -More informations at: https://develop.uap-core.de/ucx/ - diff --git a/ucx/array.c b/ucx/array.c deleted file mode 100644 index 0592fc6..0000000 --- a/ucx/array.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2019 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. - */ - -#define _GNU_SOURCE /* we want to use qsort_r(), if available */ -#define __STDC_WANT_LIB_EXT1__ 1 /* use qsort_s, if available */ - - -#include "ucx/array.h" -#include "ucx/utils.h" - -#include -#include -#include - -#ifndef UCX_ARRAY_DISABLE_QSORT -#ifdef __GLIBC__ -#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) -#define ucx_array_sort_impl qsort_r -#endif /* glibc version >= 2.8 */ -#elif /* not __GLIBC__ */ defined(__APPLE__) || defined(__FreeBSD__) -#define ucx_array_sort_impl ucx_qsort_r -#define USE_UCX_QSORT_R -#elif /* not (__APPLE || __FreeBSD__) */ defined(__sun) -#if __STDC_VERSION__ >= 201112L -#define ucx_array_sort_impl qsort_s -#endif -#endif /* __GLIBC__, __APLE__, __FreeBSD__, __sun */ -#endif /* UCX_ARRAY_DISABLE_QSORT */ - -#ifndef ucx_array_sort_impl -#define ucx_array_sort_impl ucx_mergesort -#endif - -static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) { - size_t required_capacity = array->capacity; - while (reqcap > required_capacity) { - if (required_capacity * 2 < required_capacity) - return 1; - required_capacity <<= 1; - } - if (ucx_array_reserve(array, required_capacity)) { - return 1; - } - return 0; -} - -int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t elmsize, size_t index, void* data) { - - if(!alloc || !capacity || !array) { - errno = EINVAL; - return 1; - } - - size_t newcapacity = *capacity; - while(index >= newcapacity) { - if(ucx_szmul(newcapacity, 2, &newcapacity)) { - errno = EOVERFLOW; - return 1; - } - } - - size_t memlen, offset; - if(ucx_szmul(newcapacity, elmsize, &memlen)) { - errno = EOVERFLOW; - return 1; - } - /* we don't need to check index*elmsize - it is smaller than memlen */ - - - void* newptr = alrealloc(alloc, *array, memlen); - if(newptr == NULL) { - errno = ENOMEM; /* we cannot assume that every allocator sets this */ - return 1; - } - *array = newptr; - *capacity = newcapacity; - - - char* dest = *array; - dest += elmsize*index; - memcpy(dest, data, elmsize); - - return 0; -} - -int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t index, void* data) { - - return ucx_array_util_set_a(alloc, array, capacity, sizeof(void*), - index, &data); -} - -UcxArray* ucx_array_new(size_t capacity, size_t elemsize) { - return ucx_array_new_a(capacity, elemsize, ucx_default_allocator()); -} - -UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize, - UcxAllocator* allocator) { - UcxArray* array = almalloc(allocator, sizeof(UcxArray)); - if(array) { - ucx_array_init_a(array, capacity, elemsize, allocator); - } - return array; -} - -void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize) { - ucx_array_init_a(array, capacity, elemsize, ucx_default_allocator()); -} - -void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize, - UcxAllocator* allocator) { - - array->allocator = allocator; - array->elemsize = elemsize; - array->size = 0; - array->data = alcalloc(allocator, capacity, elemsize); - - if (array->data) { - array->capacity = capacity; - } else { - array->capacity = 0; - } -} - -int ucx_array_clone(UcxArray* dest, UcxArray const* src) { - if (ucx_array_ensurecap(dest, src->capacity)) { - return 1; - } - - dest->elemsize = src->elemsize; - dest->size = src->size; - - if (dest->data) { - memcpy(dest->data, src->data, src->size*src->elemsize); - } - - return 0; -} - -int ucx_array_equals(UcxArray const *array1, UcxArray const *array2, - cmp_func cmpfnc, void* data) { - - if (array1->size != array2->size || array1->elemsize != array2->elemsize) { - return 0; - } else { - if (array1->size == 0) - return 1; - - size_t elemsize; - if (cmpfnc == NULL) { - cmpfnc = ucx_cmp_mem; - elemsize = array1->elemsize; - data = &elemsize; - } - - for (size_t i = 0 ; i < array1->size ; i++) { - int r = cmpfnc( - ucx_array_at(array1, i), - ucx_array_at(array2, i), - data); - if (r != 0) - return 0; - } - return 1; - } -} - -void ucx_array_destroy(UcxArray *array) { - if(array->data) - alfree(array->allocator, array->data); - array->data = NULL; - array->capacity = array->size = 0; -} - -void ucx_array_free(UcxArray *array) { - ucx_array_destroy(array); - alfree(array->allocator, array); -} - -int ucx_array_append_from(UcxArray *array, void *data, size_t count) { - if (ucx_array_ensurecap(array, array->size + count)) - return 1; - - void* dest = ucx_array_at(array, array->size); - if (data) { - memcpy(dest, data, array->elemsize*count); - } else { - memset(dest, 0, array->elemsize*count); - } - array->size += count; - - return 0; -} - -int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) { - if (ucx_array_ensurecap(array, array->size + count)) - return 1; - - if (array->size > 0) { - void *dest = ucx_array_at(array, count); - memmove(dest, array->data, array->elemsize*array->size); - } - - if (data) { - memcpy(array->data, data, array->elemsize*count); - } else { - memset(array->data, 0, array->elemsize*count); - } - array->size += count; - - return 0; -} - -int ucx_array_set_from(UcxArray *array, size_t index, - void *data, size_t count) { - if (ucx_array_ensurecap(array, index + count)) - return 1; - - if (index+count > array->size) { - array->size = index+count; - } - - void *dest = ucx_array_at(array, index); - if (data) { - memcpy(dest, data, array->elemsize*count); - } else { - memset(dest, 0, array->elemsize*count); - } - - return 0; -} - -int ucx_array_concat(UcxArray *array1, const UcxArray *array2) { - - if (array1->elemsize != array2->elemsize) - return 1; - - size_t capacity = array1->capacity+array2->capacity; - - if (array1->capacity < capacity) { - if (ucx_array_reserve(array1, capacity)) { - return 1; - } - } - - void* dest = ucx_array_at(array1, array1->size); - memcpy(dest, array2->data, array2->size*array2->elemsize); - - array1->size += array2->size; - - return 0; -} - -void *ucx_array_at(UcxArray const *array, size_t index) { - char* memory = array->data; - char* loc = memory + index*array->elemsize; - return loc; -} - -size_t ucx_array_find(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data) { - - size_t elemsize; - if (cmpfnc == NULL) { - cmpfnc = ucx_cmp_mem; - elemsize = array->elemsize; - data = &elemsize; - } - - if (array->size > 0) { - for (size_t i = 0 ; i < array->size ; i++) { - void* ptr = ucx_array_at(array, i); - if (cmpfnc(ptr, elem, data) == 0) { - return i; - } - } - return array->size; - } else { - return 0; - } -} - -int ucx_array_contains(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data) { - return ucx_array_find(array, elem, cmpfnc, data) != array->size; -} - -static void ucx_mergesort_merge(void *arrdata,size_t elemsize, - cmp_func cmpfnc, void *data, - size_t start, size_t mid, size_t end) { - - char* array = arrdata; - - size_t rightstart = mid + 1; - - if (cmpfnc(array + mid*elemsize, - array + rightstart*elemsize, data) <= 0) { - /* already sorted */ - return; - } - - /* we need memory for one element */ - void *value = malloc(elemsize); - - while (start <= mid && rightstart <= end) { - if (cmpfnc(array + start*elemsize, - array + rightstart*elemsize, data) <= 0) { - start++; - } else { - /* save the value from the right */ - memcpy(value, array + rightstart*elemsize, elemsize); - - /* shift all left elements one element to the right */ - size_t shiftcount = rightstart-start; - void *startptr = array + start*elemsize; - void *dest = array + (start+1)*elemsize; - memmove(dest, startptr, shiftcount*elemsize); - - /* bring the first value from the right to the left */ - memcpy(startptr, value, elemsize); - - start++; - mid++; - rightstart++; - } - } - - /* free the temporary memory */ - free(value); -} - -static void ucx_mergesort_impl(void *arrdata, size_t elemsize, - cmp_func cmpfnc, void *data, size_t l, size_t r) { - if (l < r) { - size_t m = l + (r - l) / 2; - - ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m); - ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r); - ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r); - } -} - -static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize, - cmp_func cmpfnc, void *data) { - - ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1); -} - -#ifdef USE_UCX_QSORT_R -struct cmpfnc_swapargs_info { - cmp_func func; - void *data; -}; - -static int cmp_func_swap_args(void *data, const void *x, const void *y) { - struct cmpfnc_swapargs_info* info = data; - return info->func(x, y, info->data); -} - -static void ucx_qsort_r(void *array, size_t count, size_t elemsize, - cmp_func cmpfnc, void *data) { - struct cmpfnc_swapargs_info info; - info.func = cmpfnc; - info.data = data; - qsort_r(array, count, elemsize, &info, cmp_func_swap_args); -} -#endif /* USE_UCX_QSORT_R */ - -void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data) { - ucx_array_sort_impl(array->data, array->size, array->elemsize, - cmpfnc, data); -} - -void ucx_array_remove(UcxArray *array, size_t index) { - array->size--; - if (index < array->size) { - void* dest = ucx_array_at(array, index); - void* src = ucx_array_at(array, index+1); - memmove(dest, src, (array->size - index)*array->elemsize); - } -} - -void ucx_array_remove_fast(UcxArray *array, size_t index) { - array->size--; - if (index < array->size) { - void* dest = ucx_array_at(array, index); - void* src = ucx_array_at(array, array->size); - memcpy(dest, src, array->elemsize); - } -} - -int ucx_array_shrink(UcxArray* array) { - void* newptr = alrealloc(array->allocator, array->data, - array->size*array->elemsize); - if (newptr) { - array->data = newptr; - array->capacity = array->size; - return 0; - } else { - return 1; - } -} - -int ucx_array_resize(UcxArray* array, size_t capacity) { - if (array->capacity >= capacity) { - void* newptr = alrealloc(array->allocator, array->data, - capacity*array->elemsize); - if (newptr) { - array->data = newptr; - array->capacity = capacity; - if (array->size > array->capacity) { - array->size = array->capacity; - } - return 0; - } else { - return 1; - } - } else { - return ucx_array_reserve(array, capacity); - } -} - -int ucx_array_reserve(UcxArray* array, size_t capacity) { - if (array->capacity > capacity) { - return 0; - } else { - void* newptr = alrealloc(array->allocator, array->data, - capacity*array->elemsize); - if (newptr) { - array->data = newptr; - array->capacity = capacity; - return 0; - } else { - return 1; - } - } -} - -int ucx_array_grow(UcxArray* array, size_t count) { - return ucx_array_reserve(array, array->size+count); -} diff --git a/ucx/avl.c b/ucx/avl.c deleted file mode 100644 index 7639b56..0000000 --- a/ucx/avl.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/avl.h" - -#include - -#define ptrcast(ptr) ((void*)(ptr)) -#define alloc_tree(al) (UcxAVLTree*) almalloc((al), sizeof(UcxAVLTree)) -#define alloc_node(al) (UcxAVLNode*) almalloc((al), sizeof(UcxAVLNode)) - -static void ucx_avl_connect(UcxAVLTree *tree, - UcxAVLNode *node, UcxAVLNode *child, intptr_t nullkey) { - if (child) { - child->parent = node; - } - // if child is NULL, nullkey decides if left or right pointer is cleared - if (tree->cmpfunc( - ptrcast(child ? child->key : nullkey), - ptrcast(node->key), tree->userdata) > 0) { - node->right = child; - } else { - node->left = child; - } - size_t lh = node->left ? node->left->height : 0; - size_t rh = node->right ? node->right->height : 0; - node->height = 1 + (lh > rh ? lh : rh); -} - -#define avlheight(node) ((node) ? (node)->height : 0) - -static UcxAVLNode* avl_rotright(UcxAVLTree *tree, UcxAVLNode *l0) { - UcxAVLNode *p = l0->parent; - UcxAVLNode *l1 = l0->left; - if (p) { - ucx_avl_connect(tree, p, l1, 0); - } else { - l1->parent = NULL; - } - ucx_avl_connect(tree, l0, l1->right, l1->key); - ucx_avl_connect(tree, l1, l0, 0); - return l1; -} - -static UcxAVLNode* avl_rotleft(UcxAVLTree *tree, UcxAVLNode *l0) { - UcxAVLNode *p = l0->parent; - UcxAVLNode *l1 = l0->right; - if (p) { - ucx_avl_connect(tree, p, l1, 0); - } else { - l1->parent = NULL; - } - ucx_avl_connect(tree, l0, l1->left, l1->key); - ucx_avl_connect(tree, l1, l0, 0); - return l1; -} - -static void ucx_avl_balance(UcxAVLTree *tree, UcxAVLNode *n) { - int lh = avlheight(n->left); - int rh = avlheight(n->right); - n->height = 1 + (lh > rh ? lh : rh); - - if (lh - rh == 2) { - UcxAVLNode *c = n->left; - if (avlheight(c->right) - avlheight(c->left) == 1) { - avl_rotleft(tree, c); - } - n = avl_rotright(tree, n); - } else if (rh - lh == 2) { - UcxAVLNode *c = n->right; - if (avlheight(c->left) - avlheight(c->right) == 1) { - avl_rotright(tree, c); - } - n = avl_rotleft(tree, n); - } - - if (n->parent) { - ucx_avl_balance(tree, n->parent); - } else { - tree->root = n; - } -} - -UcxAVLTree *ucx_avl_new(cmp_func cmpfunc) { - return ucx_avl_new_a(cmpfunc, ucx_default_allocator()); -} - -UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator) { - UcxAVLTree* tree = alloc_tree(allocator); - if (tree) { - tree->allocator = allocator; - tree->cmpfunc = cmpfunc; - tree->root = NULL; - tree->userdata = NULL; - } - - return tree; -} - -static void ucx_avl_free_node(UcxAllocator *al, UcxAVLNode *node) { - if (node) { - ucx_avl_free_node(al, node->left); - ucx_avl_free_node(al, node->right); - alfree(al, node); - } -} - -void ucx_avl_free(UcxAVLTree *tree) { - UcxAllocator *al = tree->allocator; - ucx_avl_free_node(al, tree->root); - alfree(al, tree); -} - -static void ucx_avl_free_content_node(UcxAllocator *al, UcxAVLNode *node, - ucx_destructor destr) { - if (node) { - ucx_avl_free_content_node(al, node->left, destr); - ucx_avl_free_content_node(al, node->right, destr); - if (destr) { - destr(node->value); - } else { - alfree(al, node->value); - } - } -} - -void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr) { - ucx_avl_free_content_node(tree->allocator, tree->root, destr); -} - -UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key) { - UcxAVLNode *n = tree->root; - int cmpresult; - while (n && (cmpresult = tree->cmpfunc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - n = cmpresult > 0 ? n->right : n->left; - } - return n; -} - -void *ucx_avl_get(UcxAVLTree *tree, intptr_t key) { - UcxAVLNode *n = ucx_avl_get_node(tree, key); - return n ? n->value : NULL; -} - -UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode) { - UcxAVLNode *n = tree->root; - UcxAVLNode *closest = NULL; - - intmax_t cmpresult; - intmax_t closest_dist; - closest_dist = mode == UCX_AVL_FIND_LOWER_BOUNDED ? INTMAX_MIN : INTMAX_MAX; - - while (n && (cmpresult = dfnc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - if (mode == UCX_AVL_FIND_CLOSEST) { - intmax_t dist = cmpresult; - if (dist < 0) dist *= -1; - if (dist < closest_dist) { - closest_dist = dist; - closest = n; - } - } else if (mode == UCX_AVL_FIND_LOWER_BOUNDED && cmpresult <= 0) { - if (cmpresult > closest_dist) { - closest_dist = cmpresult; - closest = n; - } - } else if (mode == UCX_AVL_FIND_UPPER_BOUNDED && cmpresult >= 0) { - if (cmpresult < closest_dist) { - closest_dist = cmpresult; - closest = n; - } - } - n = cmpresult > 0 ? n->right : n->left; - } - return n ? n : closest; -} - -void *ucx_avl_find(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode) { - UcxAVLNode *n = ucx_avl_find_node(tree, key, dfnc, mode); - return n ? n->value : NULL; -} - -int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value) { - return ucx_avl_put_s(tree, key, value, NULL); -} - -int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, - void **oldvalue) { - if (tree->root) { - UcxAVLNode *n = tree->root; - int cmpresult; - while ((cmpresult = tree->cmpfunc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - UcxAVLNode *m = cmpresult > 0 ? n->right : n->left; - if (m) { - n = m; - } else { - break; - } - } - - if (cmpresult) { - UcxAVLNode* e = alloc_node(tree->allocator); - if (e) { - e->key = key; e->value = value; e->height = 1; - e->parent = e->left = e->right = NULL; - ucx_avl_connect(tree, n, e, 0); - ucx_avl_balance(tree, n); - return 0; - } else { - return 1; - } - } else { - if (oldvalue) { - *oldvalue = n->value; - } - n->value = value; - return 0; - } - } else { - tree->root = alloc_node(tree->allocator); - if (tree->root) { - tree->root->key = key; tree->root->value = value; - tree->root->height = 1; - tree->root->parent = tree->root->left = tree->root->right = NULL; - - if (oldvalue) { - *oldvalue = NULL; - } - - return 0; - } else { - return 1; - } - } -} - -int ucx_avl_remove(UcxAVLTree *tree, intptr_t key) { - return ucx_avl_remove_s(tree, key, NULL, NULL); -} - -int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node) { - return ucx_avl_remove_s(tree, node->key, NULL, NULL); -} - -int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key, - intptr_t *oldkey, void **oldvalue) { - - UcxAVLNode *n = tree->root; - int cmpresult; - while (n && (cmpresult = tree->cmpfunc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - n = cmpresult > 0 ? n->right : n->left; - } - if (n) { - if (oldkey) { - *oldkey = n->key; - } - if (oldvalue) { - *oldvalue = n->value; - } - - UcxAVLNode *p = n->parent; - if (n->left && n->right) { - UcxAVLNode *s = n->right; - while (s->left) { - s = s->left; - } - ucx_avl_connect(tree, s->parent, s->right, s->key); - n->key = s->key; n->value = s->value; - p = s->parent; - alfree(tree->allocator, s); - } else { - if (p) { - ucx_avl_connect(tree, p, n->right ? n->right:n->left, n->key); - } else { - tree->root = n->right ? n->right : n->left; - if (tree->root) { - tree->root->parent = NULL; - } - } - alfree(tree->allocator, n); - } - - if (p) { - ucx_avl_balance(tree, p); - } - - return 0; - } else { - return 1; - } -} - -static size_t ucx_avl_countn(UcxAVLNode *node) { - if (node) { - return 1 + ucx_avl_countn(node->left) + ucx_avl_countn(node->right); - } else { - return 0; - } -} - -size_t ucx_avl_count(UcxAVLTree *tree) { - return ucx_avl_countn(tree->root); -} - -UcxAVLNode* ucx_avl_pred(UcxAVLNode* node) { - if (node->left) { - UcxAVLNode* n = node->left; - while (n->right) { - n = n->right; - } - return n; - } else { - UcxAVLNode* n = node; - while (n->parent) { - if (n->parent->right == n) { - return n->parent; - } else { - n = n->parent; - } - } - return NULL; - } -} - -UcxAVLNode* ucx_avl_succ(UcxAVLNode* node) { - if (node->right) { - UcxAVLNode* n = node->right; - while (n->left) { - n = n->left; - } - return n; - } else { - UcxAVLNode* n = node; - while (n->parent) { - if (n->parent->left == n) { - return n->parent; - } else { - n = n->parent; - } - } - return NULL; - } -} diff --git a/ucx/logging.c b/ucx/logging.c deleted file mode 100644 index d6fdce0..0000000 --- a/ucx/logging.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/logging.h" - -#include -#include -#include -#include - -UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask) { - UcxLogger *logger = (UcxLogger*) malloc(sizeof(UcxLogger)); - if (logger != NULL) { - logger->stream = stream; - logger->writer = (write_func)fwrite; - logger->dateformat = (char*) "%F %T %z "; - logger->level = level; - logger->mask = mask; - logger->levels = ucx_map_new(8); - - unsigned int l; - l = UCX_LOGGER_ERROR; - ucx_map_int_put(logger->levels, l, (void*) "[ERROR]"); - l = UCX_LOGGER_WARN; - ucx_map_int_put(logger->levels, l, (void*) "[WARNING]"); - l = UCX_LOGGER_INFO; - ucx_map_int_put(logger->levels, l, (void*) "[INFO]"); - l = UCX_LOGGER_DEBUG; - ucx_map_int_put(logger->levels, l, (void*) "[DEBUG]"); - l = UCX_LOGGER_TRACE; - ucx_map_int_put(logger->levels, l, (void*) "[TRACE]"); - } - - return logger; -} - -void ucx_logger_free(UcxLogger *logger) { - ucx_map_free(logger->levels); - free(logger); -} - -// estimated max. message length (documented) -#define UCX_LOGGER_MSGMAX 4096 - -void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file, - const unsigned int line, const char *format, ...) { - if (level <= logger->level) { - char msg[UCX_LOGGER_MSGMAX]; - const char *text; - size_t k = 0; - size_t n; - - if ((logger->mask & UCX_LOGGER_LEVEL) > 0) { - text = (const char*) ucx_map_int_get(logger->levels, level); - if (!text) { - text = "[UNKNOWN]"; - } - n = strlen(text); - n = n > 256 ? 256 : n; - memcpy(msg+k, text, n); - k += n; - msg[k++] = ' '; - } - if ((logger->mask & UCX_LOGGER_TIMESTAMP) > 0) { - time_t now = time(NULL); - k += strftime(msg+k, 128, logger->dateformat, localtime(&now)); - } - if ((logger->mask & UCX_LOGGER_SOURCE) > 0) { - char *fpart = strrchr(file, '/'); - if (fpart) file = fpart+1; - fpart = strrchr(file, '\\'); - if (fpart) file = fpart+1; - n = strlen(file); - memcpy(msg+k, file, n); - k += n; - k += sprintf(msg+k, ":%u ", line); - } - - if (k > 0) { - msg[k++] = '-'; msg[k++] = ' '; - } - - va_list args; - va_start (args, format); - k += vsnprintf(msg+k, UCX_LOGGER_MSGMAX-k-1, format, args); - va_end (args); - - msg[k++] = '\n'; - - logger->writer(msg, 1, k, logger->stream); - } -} diff --git a/ucx/stack.c b/ucx/stack.c deleted file mode 100644 index 467233e..0000000 --- a/ucx/stack.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/stack.h" - -#include - -static size_t ucx_stack_align(size_t n) { - int align = n % sizeof(void*); - if (align) { - n += sizeof(void*) - align; - } - return n; -} - -void ucx_stack_init(UcxStack *stack, char* space, size_t size) { - stack->size = size - size % sizeof(void*); - stack->space = space; - stack->top = NULL; - - stack->allocator.pool = stack; - stack->allocator.malloc = (ucx_allocator_malloc) ucx_stack_malloc; - stack->allocator.calloc = (ucx_allocator_calloc) ucx_stack_calloc; - stack->allocator.realloc = (ucx_allocator_realloc) ucx_stack_realloc; - stack->allocator.free = (ucx_allocator_free) ucx_stack_free; -} - -void *ucx_stack_malloc(UcxStack *stack, size_t n) { - - if (ucx_stack_avail(stack) < ucx_stack_align(n)) { - return NULL; - } else { - char *prev = stack->top; - if (stack->top) { - stack->top += ucx_stack_align(ucx_stack_topsize(stack)); - } else { - stack->top = stack->space; - } - - ((struct ucx_stack_metadata*)stack->top)->prev = prev; - ((struct ucx_stack_metadata*)stack->top)->size = n; - stack->top += sizeof(struct ucx_stack_metadata); - - return stack->top; - } -} - -void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize) { - void *mem = ucx_stack_malloc(stack, nelem*elsize); - memset(mem, 0, nelem*elsize); - return mem; -} - -void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n) { - if (ptr == stack->top) { - if (stack->size - (stack->top - stack->space) < ucx_stack_align(n)) { - return NULL; - } else { - ((struct ucx_stack_metadata*)stack->top - 1)->size = n; - return ptr; - } - } else { - if (ucx_stack_align(((struct ucx_stack_metadata*)ptr - 1)->size) < - ucx_stack_align(n)) { - void *nptr = ucx_stack_malloc(stack, n); - if (nptr) { - memcpy(nptr, ptr, n); - ucx_stack_free(stack, ptr); - - return nptr; - } else { - return NULL; - } - } else { - ((struct ucx_stack_metadata*)ptr - 1)->size = n; - return ptr; - } - } -} - -void ucx_stack_free(UcxStack *stack, void *ptr) { - if (ptr == stack->top) { - stack->top = ((struct ucx_stack_metadata*) stack->top - 1)->prev; - } else { - struct ucx_stack_metadata *next = (struct ucx_stack_metadata*)( - (char*)ptr + - ucx_stack_align(((struct ucx_stack_metadata*) ptr - 1)->size) - ); - next->prev = ((struct ucx_stack_metadata*) ptr - 1)->prev; - } -} - -void ucx_stack_popn(UcxStack *stack, void *dest, size_t n) { - if (ucx_stack_empty(stack)) { - return; - } - - if (dest) { - size_t len = ucx_stack_topsize(stack); - if (len > n) { - len = n; - } - - memcpy(dest, stack->top, len); - } - - ucx_stack_free(stack, stack->top); -} - -size_t ucx_stack_avail(UcxStack *stack) { - size_t avail = ((stack->top ? (stack->size - - (stack->top - stack->space) - - ucx_stack_align(ucx_stack_topsize(stack))) - : stack->size)); - - if (avail > sizeof(struct ucx_stack_metadata)) { - return avail - sizeof(struct ucx_stack_metadata); - } else { - return 0; - } -} - -void *ucx_stack_push(UcxStack *stack, size_t n, const void *data) { - void *space = ucx_stack_malloc(stack, n); - if (space) { - memcpy(space, data, n); - } - return space; -} - -void *ucx_stack_pusharr(UcxStack *stack, - size_t nelem, size_t elsize, const void *data) { - - // skip the memset by using malloc - void *space = ucx_stack_malloc(stack, nelem*elsize); - if (space) { - memcpy(space, data, nelem*elsize); - } - return space; -} diff --git a/ucx/test.c b/ucx/test.c deleted file mode 100644 index 20b80b4..0000000 --- a/ucx/test.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/test.h" - -UcxTestSuite* ucx_test_suite_new() { - UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite)); - if (suite != NULL) { - suite->success = 0; - suite->failure = 0; - suite->tests = NULL; - } - - return suite; -} - -void ucx_test_suite_free(UcxTestSuite* suite) { - UcxTestList *l = suite->tests; - while (l != NULL) { - UcxTestList *e = l; - l = l->next; - free(e); - } - free(suite); -} - -int ucx_test_register(UcxTestSuite* suite, UcxTest test) { - if (suite->tests) { - UcxTestList *newelem = (UcxTestList*) malloc(sizeof(UcxTestList)); - if (newelem) { - newelem->test = test; - newelem->next = NULL; - - UcxTestList *last = suite->tests; - while (last->next) { - last = last->next; - } - last->next = newelem; - - return EXIT_SUCCESS; - } else { - return EXIT_FAILURE; - } - } else { - suite->tests = (UcxTestList*) malloc(sizeof(UcxTestList)); - if (suite->tests) { - suite->tests->test = test; - suite->tests->next = NULL; - - return EXIT_SUCCESS; - } else { - return EXIT_FAILURE; - } - } -} - -void ucx_test_run(UcxTestSuite* suite, FILE* output) { - suite->success = 0; - suite->failure = 0; - for (UcxTestList* elem = suite->tests ; elem ; elem = elem->next) { - elem->test(suite, output); - } - fwrite("\nAll test completed.\n", 1, 21, output); - fprintf(output, " Total: %u\n Success: %u\n Failure: %u\n", - suite->success+suite->failure, suite->success, suite->failure); -} diff --git a/ucx/utils.c b/ucx/utils.c deleted file mode 100644 index 101c33f..0000000 --- a/ucx/utils.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/utils.h" - -#include -#include -#include -#include - -/* COPY FUCNTIONS */ -void* ucx_strcpy(const void* s, void* data) { - const char *str = (const char*) s; - size_t n = 1+strlen(str); - char *cpy = (char*) malloc(n); - memcpy(cpy, str, n); - return cpy; -} - -void* ucx_memcpy(const void* m, void* n) { - size_t k = *((size_t*)n); - void *cpy = malloc(k); - memcpy(cpy, m, k); - return cpy; -} - -size_t ucx_stream_bncopy(void *src, void *dest, read_func readfnc, - write_func writefnc, char* buf, size_t bufsize, size_t n) { - if(n == 0 || bufsize == 0) { - return 0; - } - - char *lbuf; - size_t ncp = 0; - - if(buf) { - lbuf = buf; - } else { - lbuf = (char*)malloc(bufsize); - if(lbuf == NULL) { - return 0; - } - } - - size_t r; - size_t rn = bufsize > n ? n : bufsize; - while((r = readfnc(lbuf, 1, rn, src)) != 0) { - r = writefnc(lbuf, 1, r, dest); - ncp += r; - n -= r; - rn = bufsize > n ? n : bufsize; - if(r == 0 || n == 0) { - break; - } - } - - if (lbuf != buf) { - free(lbuf); - } - - return ncp; -} - -/* COMPARE FUNCTIONS */ - -int ucx_cmp_str(const void *s1, const void *s2, void *data) { - return strcmp((const char*)s1, (const char*)s2); -} - -int ucx_cmp_strn(const void *s1, const void *s2, void *n) { - return strncmp((const char*)s1, (const char*)s2, *((size_t*) n)); -} - -int ucx_cmp_sstr(const void *s1, const void *s2, void *data) { - sstr_t a = *(const sstr_t*) s1; - sstr_t b = *(const sstr_t*) s2; - return sstrcmp(a, b); -} - -int ucx_cmp_int(const void *i1, const void *i2, void *data) { - int a = *((const int*) i1); - int b = *((const int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_longint(const void *i1, const void *i2, void *data) { - long int a = *((const long int*) i1); - long int b = *((const long int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_longlong(const void *i1, const void *i2, void *data) { - long long a = *((const long long*) i1); - long long b = *((const long long*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_int16(const void *i1, const void *i2, void *data) { - int16_t a = *((const int16_t*) i1); - int16_t b = *((const int16_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_int32(const void *i1, const void *i2, void *data) { - int32_t a = *((const int32_t*) i1); - int32_t b = *((const int32_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_int64(const void *i1, const void *i2, void *data) { - int64_t a = *((const int64_t*) i1); - int64_t b = *((const int64_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_uint(const void *i1, const void *i2, void *data) { - unsigned int a = *((const unsigned int*) i1); - unsigned int b = *((const unsigned int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_ulongint(const void *i1, const void *i2, void *data) { - unsigned long int a = *((const unsigned long int*) i1); - unsigned long int b = *((const unsigned long int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_ulonglong(const void *i1, const void *i2, void *data) { - unsigned long long a = *((const unsigned long long*) i1); - unsigned long long b = *((const unsigned long long*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_uint16(const void *i1, const void *i2, void *data) { - uint16_t a = *((const uint16_t*) i1); - uint16_t b = *((const uint16_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_uint32(const void *i1, const void *i2, void *data) { - uint32_t a = *((const uint32_t*) i1); - uint32_t b = *((const uint32_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_uint64(const void *i1, const void *i2, void *data) { - uint64_t a = *((const uint64_t*) i1); - uint64_t b = *((const uint64_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -intmax_t ucx_dist_int(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int*) i1); - intmax_t b = *((const int*) i2); - return a - b; -} - -intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data) { - intmax_t a = *((const long int*) i1); - intmax_t b = *((const long int*) i2); - return a - b; -} - -intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data) { - intmax_t a = *((const long long*) i1); - intmax_t b = *((const long long*) i2); - return a - b; -} - -intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int16_t*) i1); - intmax_t b = *((const int16_t*) i2); - return a - b; -} - -intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int32_t*) i1); - intmax_t b = *((const int32_t*) i2); - return a - b; -} - -intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int64_t*) i1); - intmax_t b = *((const int64_t*) i2); - return a - b; -} - -intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const unsigned int*) i1); - uintmax_t b = *((const unsigned int*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const unsigned long int*) i1); - uintmax_t b = *((const unsigned long int*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const unsigned long long*) i1); - uintmax_t b = *((const unsigned long long*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const uint16_t*) i1); - uintmax_t b = *((const uint16_t*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const uint32_t*) i1); - uintmax_t b = *((const uint32_t*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_uint64(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const uint64_t*) i1); - uintmax_t b = *((const uint64_t*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -int ucx_cmp_float(const void *f1, const void *f2, void *epsilon) { - float a = *((const float*) f1); - float b = *((const float*) f2); - float e = !epsilon ? 1e-6f : *((float*)epsilon); - if (fabsf(a - b) < e) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_double(const void *d1, const void *d2, void *epsilon) { - double a = *((const double*) d1); - double b = *((const double*) d2); - double e = !epsilon ? 1e-14 : *((double*)epsilon); - if (fabs(a - b) < e) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data) { - const intptr_t p1 = (const intptr_t) ptr1; - const intptr_t p2 = (const intptr_t) ptr2; - if (p1 == p2) { - return 0; - } else { - return p1 < p2 ? -1 : 1; - } -} - -int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n) { - return memcmp(ptr1, ptr2, *((size_t*)n)); -} - -/* PRINTF FUNCTIONS */ - -#ifdef va_copy -#define UCX_PRINTF_BUFSIZE 256 -#else -#pragma message("WARNING: C99 va_copy macro not supported by this platform" \ - " - limiting ucx_*printf to 2 KiB") -#define UCX_PRINTF_BUFSIZE 0x800 -#endif - -int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...) { - int ret; - va_list ap; - va_start(ap, fmt); - ret = ucx_vfprintf(stream, wfc, fmt, ap); - va_end(ap); - return ret; -} - -int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap) { - char buf[UCX_PRINTF_BUFSIZE]; -#ifdef va_copy - va_list ap2; - va_copy(ap2, ap); - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret < 0) { - return ret; - } else if (ret < UCX_PRINTF_BUFSIZE) { - return (int)wfc(buf, 1, ret, stream); - } else { - if (ret == INT_MAX) { - errno = ENOMEM; - return -1; - } - - int len = ret + 1; - char *newbuf = (char*)malloc(len); - if (!newbuf) { - return -1; - } - - ret = vsnprintf(newbuf, len, fmt, ap2); - if (ret > 0) { - ret = (int)wfc(newbuf, 1, ret, stream); - } - free(newbuf); - } - return ret; -#else - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret < 0) { - return ret; - } else if (ret < UCX_PRINTF_BUFSIZE) { - return (int)wfc(buf, 1, ret, stream); - } else { - errno = ENOMEM; - return -1; - } -#endif -} - -sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...) { - va_list ap; - sstr_t ret; - va_start(ap, fmt); - ret = ucx_vasprintf(allocator, fmt, ap); - va_end(ap); - return ret; -} - -sstr_t ucx_vasprintf(UcxAllocator *a, const char *fmt, va_list ap) { - sstr_t s; - s.ptr = NULL; - s.length = 0; - char buf[UCX_PRINTF_BUFSIZE]; -#ifdef va_copy - va_list ap2; - va_copy(ap2, ap); - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) { - s.ptr = (char*)almalloc(a, ret + 1); - if (s.ptr) { - s.length = (size_t)ret; - memcpy(s.ptr, buf, ret); - s.ptr[s.length] = '\0'; - } - } else if (ret == INT_MAX) { - errno = ENOMEM; - } else { - int len = ret + 1; - s.ptr = (char*)almalloc(a, len); - if (s.ptr) { - ret = vsnprintf(s.ptr, len, fmt, ap2); - if (ret < 0) { - free(s.ptr); - s.ptr = NULL; - } else { - s.length = (size_t)ret; - } - } - } -#else - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) { - s.ptr = (char*)almalloc(a, ret + 1); - if (s.ptr) { - s.length = (size_t)ret; - memcpy(s.ptr, buf, ret); - s.ptr[s.length] = '\0'; - } - } else { - errno = ENOMEM; - } -#endif - return s; -} diff --git a/ui/Makefile b/ui/Makefile index a0d1148..6905986 100644 --- a/ui/Makefile +++ b/ui/Makefile @@ -33,7 +33,7 @@ OBJ_DIR = ../build/ include common/objs.mk -UI_LIB = ../build/lib/libuitk$(LIB_EXT) +UI_LIB = ../build/lib/$(LIB_PREFIX)uitk$(LIB_EXT) include $(TOOLKIT)/objs.mk OBJ = $(TOOLKITOBJS) $(COMMONOBJS) @@ -42,6 +42,6 @@ all: $(UI_LIB) include $(TOOLKIT)/Makefile -$(COMMON_OBJPRE)%.o: common/%.c +$(COMMON_OBJPRE)uic_%$(OBJ_EXT): common/%.c $(CC) -o $@ -c -I../ucx/ $(CFLAGS) $(TK_CFLAGS) $< diff --git a/ui/common/condvar.c b/ui/common/condvar.c index c61e70c..16f4954 100644 --- a/ui/common/condvar.c +++ b/ui/common/condvar.c @@ -26,6 +26,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifndef _WIN32 + #include "condvar.h" #include @@ -68,3 +70,5 @@ void ui_condvar_destroy(UiCondVar *var) { free(p); } + +#endif \ No newline at end of file diff --git a/ui/common/context.c b/ui/common/context.c index f7dfdd3..db9d850 100644 --- a/ui/common/context.c +++ b/ui/common/context.c @@ -58,6 +58,7 @@ UiContext* uic_context(UiObject *toplevel, CxMempool *mp) { memset(ctx, 0, sizeof(UiContext)); ctx->mp = mp; ctx->allocator = mp->allocator; + ctx->destroy_handler = cxArrayListCreate(ctx->allocator, NULL, sizeof(UiDestroyHandler), 16); ctx->obj = toplevel; ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16); @@ -82,6 +83,13 @@ UiContext* uic_root_context(UiContext *ctx) { return ctx->parent ? uic_root_context(ctx->parent) : ctx; } +void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *data) { + UiDestroyHandler handler; + handler.destructor = func; + handler.data = data; + cxListAdd(ctx->destroy_handler, &handler); +} + void uic_context_prepare_close(UiContext *ctx) { cxListClear(ctx->groups); cxListClear(ctx->group_widgets); @@ -92,6 +100,7 @@ void uic_context_attach_document(UiContext *ctx, void *document) { ctx->document = 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 @@ -111,7 +120,7 @@ void uic_context_attach_document(UiContext *ctx, void *document) { } } - var_ctx = ctx->parent; + var_ctx = var_ctx->parent; } } @@ -119,11 +128,12 @@ static void uic_context_unbind_vars(UiContext *ctx) { CxMapIterator mi = cxMapIterator(ctx->vars); cx_foreach(CxMapEntry*, entry, mi) { UiVar *var = entry->value; - if(var->from && var->from_ctx) { + // var->from && var->from_ctx && var->from_ctx != ctx + if(var->from) { uic_save_var2(var); uic_copy_binding(var, var->from, FALSE); - cxMapPut(var->from_ctx->vars_unbound, *entry->key, var->from); - var->from_ctx = ctx; + cxMapPut(var->from->from_ctx->vars_unbound, *entry->key, var->from); + var->from = NULL; } } @@ -138,8 +148,8 @@ static void uic_context_unbind_vars(UiContext *ctx) { void uic_context_detach_document2(UiContext *ctx, void *document) { // find the document in the documents list - ssize_t docIndex = cxListFind(ctx->documents, document); - if(docIndex < 0) { + size_t docIndex = cxListFind(ctx->documents, document); + if(!cxListIndexValid(ctx->documents, docIndex)) { return; } @@ -148,6 +158,7 @@ void uic_context_detach_document2(UiContext *ctx, void *document) { UiContext *docctx = ui_document_context(document); uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent + docctx->parent = NULL; } void uic_context_detach_all(UiContext *ctx) { @@ -207,6 +218,7 @@ UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { var = ui_malloc(ctx, sizeof(UiVar)); var->type = type; var->value = uic_create_value(ctx, type); + var->original_value = NULL; var->from = NULL; var->from_ctx = ctx; @@ -225,6 +237,7 @@ UiVar* uic_create_value_var(UiContext* ctx, void* value) { var->from = NULL; var->from_ctx = ctx; var->value = value; + var->original_value = NULL; var->type = UI_VAR_SPECIAL; return var; } @@ -284,12 +297,23 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { } void *fromvalue = from->value; + void *tovalue = to->value; // update var if(copytodoc) { - to->from = from; - to->from_ctx = from->from_ctx; + to->from = from; // from which UiVar are the bindings copied + from->original_value = fromvalue; // save original value otherwise it would be lost + // widgets store a reference to the UiVar with their value + // the UiVar object must be updated to contain the current value object + from->value = tovalue; + } else { + if(to->original_value) { + to->value = to->original_value; + tovalue = to->value; + } } + ui_setop_enable(TRUE); + // copy binding // we don't copy the observer, because the from var has never one switch(from->type) { @@ -297,7 +321,7 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { case UI_VAR_SPECIAL: break; case UI_VAR_INTEGER: { UiInteger *f = fromvalue; - UiInteger *t = to->value; + UiInteger *t = tovalue; if(!f->obj) break; uic_int_copy(f, t); t->set(t, t->value); @@ -305,7 +329,7 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { } case UI_VAR_DOUBLE: { UiDouble *f = fromvalue; - UiDouble *t = to->value; + UiDouble *t = tovalue; if(!f->obj) break; uic_double_copy(f, t); t->set(t, t->value); @@ -313,47 +337,32 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { } case UI_VAR_STRING: { UiString *f = fromvalue; - UiString *t = to->value; + UiString *t = tovalue; if(!f->obj) break; uic_string_copy(f, t); char *tvalue = t->value.ptr ? t->value.ptr : ""; + char *fvalue = f->value.ptr ? f->value.ptr : ""; t->set(t, tvalue); break; } case UI_VAR_TEXT: { UiText *f = fromvalue; - UiText *t = to->value; + UiText *t = tovalue; if(!f->obj) break; uic_text_copy(f, t); - char *tvalue = t->value.ptr ? t->value.ptr : ""; - t->set(t, tvalue); - t->setposition(t, t->pos); + t->restore(t); break; } - case UI_VAR_LIST: { - // TODO: not sure how correct this is - - UiList *f = from->value; - UiList *t = to->value; - if (f->obj) { - t->obj = f->obj; - t->update = f->update; - t->getselection = f->getselection; - t->setselection = f->setselection; - } - - UiVar tmp = *from; - *from = *to; - *to = tmp; - - UiList* t2 = to->value; - ui_notify(t2->observers, NULL); - + case UI_VAR_LIST: { + UiList *f = fromvalue; + UiList *t = tovalue; + uic_list_copy(f, t); + ui_list_update(t); break; } case UI_VAR_RANGE: { UiRange *f = fromvalue; - UiRange *t = to->value; + UiRange *t = tovalue; if(!f->obj) break; uic_range_copy(f, t); t->setextent(t, t->extent); @@ -363,13 +372,15 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { } case UI_VAR_GENERIC: { UiGeneric *f = fromvalue; - UiGeneric *t = to->value; + UiGeneric *t = tovalue; if(!f->obj) break; uic_generic_copy(f, t); t->set(t, t->value, t->type); break; } } + + ui_setop_enable(FALSE); } void uic_save_var2(UiVar *var) { @@ -466,6 +477,10 @@ void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata) { } UIEXPORT void ui_context_destroy(UiContext *ctx) { + CxIterator i = cxListIterator(ctx->destroy_handler); + cx_foreach(UiDestroyHandler *, h, i) { + h->destructor(h->data); + } cxMempoolFree(ctx->mp); } diff --git a/ui/common/context.h b/ui/common/context.h index fdcd6a8..e0ce3de 100644 --- a/ui/common/context.h +++ b/ui/common/context.h @@ -40,14 +40,13 @@ extern "C" { #endif -typedef struct UiVar UiVar; -typedef struct UiListPtr UiListPtr; -typedef struct UiListVar UiListVar; -typedef struct UiGroupWidget UiGroupWidget; +typedef struct UiVar UiVar; +typedef struct UiListPtr UiListPtr; +typedef struct UiListVar UiListVar; +typedef struct UiGroupWidget UiGroupWidget; +typedef struct UiDestroyHandler UiDestroyHandler; -typedef enum UiVarType UiVarType; - -enum UiVarType { +typedef enum UiVarType { UI_VAR_SPECIAL = 0, UI_VAR_INTEGER, UI_VAR_DOUBLE, @@ -56,13 +55,14 @@ enum UiVarType { UI_VAR_LIST, UI_VAR_RANGE, UI_VAR_GENERIC -}; +} UiVarType; struct UiContext { UiContext *parent; UiObject *obj; CxMempool *mp; const CxAllocator *allocator; + CxList *destroy_handler; void *document; CxList *documents; @@ -95,8 +95,9 @@ struct UiContext { // UiVar replacement, rename it to UiVar when finished struct UiVar { void *value; + void *original_value; UiVarType type; - UiVar *from; + UiVar *from; UiContext *from_ctx; }; @@ -107,11 +108,17 @@ struct UiGroupWidget { int numgroups; }; +struct UiDestroyHandler { + cx_destructor_func destructor; + void *data; +}; + void uic_init_global_context(void); UiContext* uic_context(UiObject *toplevel, CxMempool *mp); UiContext* uic_root_context(UiContext *ctx); +void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *data); void uic_context_set_document(UiContext *ctx, void *document); // deprecated void uic_context_detach_document(UiContext *ctx); // deprecated @@ -119,6 +126,8 @@ void uic_context_prepare_close(UiContext *ctx); void uic_context_attach_document(UiContext *ctx, void *document); void uic_context_detach_document2(UiContext *ctx, void *document); +void uic_context_attach_context(UiContext *ctx, UiContext *doc_ctx); +void uic_context_detach_context(UiContext *ctx, UiContext *doc_ctx); void uic_context_detach_all(UiContext *ctx); UiVar* uic_get_var(UiContext *ctx, const char *name); diff --git a/ui/common/document.c b/ui/common/document.c index d36014d..07177bd 100644 --- a/ui/common/document.c +++ b/ui/common/document.c @@ -83,7 +83,7 @@ void* ui_get_subdocument(void *document) { } void* ui_document_new(size_t size) { - CxMempool *mp = cxMempoolCreate(256, NULL); + CxMempool *mp = cxMempoolCreateSimple(256); const CxAllocator *a = mp->allocator; UiContext *ctx = uic_context(NULL, mp); diff --git a/ui/common/objs.mk b/ui/common/objs.mk index b8980af..bc1b385 100644 --- a/ui/common/objs.mk +++ b/ui/common/objs.mk @@ -29,18 +29,18 @@ COMMON_SRC_DIR = ui/common/ COMMON_OBJPRE = $(OBJ_DIR)$(COMMON_SRC_DIR) -COMMON_OBJ = context.o -COMMON_OBJ += document.o -COMMON_OBJ += object.o -COMMON_OBJ += types.o -COMMON_OBJ += menu.o -COMMON_OBJ += properties.o -COMMON_OBJ += menu.o -COMMON_OBJ += toolbar.o -COMMON_OBJ += ucx_properties.o -COMMON_OBJ += threadpool.o -COMMON_OBJ += condvar.o +COMMON_OBJ = context$(OBJ_EXT) +COMMON_OBJ += document$(OBJ_EXT) +COMMON_OBJ += object$(OBJ_EXT) +COMMON_OBJ += types$(OBJ_EXT) +COMMON_OBJ += menu$(OBJ_EXT) +COMMON_OBJ += properties$(OBJ_EXT) +COMMON_OBJ += menu$(OBJ_EXT) +COMMON_OBJ += toolbar$(OBJ_EXT) +COMMON_OBJ += ucx_properties$(OBJ_EXT) +COMMON_OBJ += threadpool$(OBJ_EXT) +COMMON_OBJ += condvar$(OBJ_EXT) -TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)%) -TOOLKITSOURCE += $(COMMON_OBJ:%.o=common/%.c) +TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)uic_%) +TOOLKITSOURCE += $(COMMON_OBJ:%$(OBJ_EXT)=common/%.c) diff --git a/ui/common/properties.c b/ui/common/properties.c index 45a9b4e..629112f 100644 --- a/ui/common/properties.c +++ b/ui/common/properties.c @@ -32,6 +32,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + #include "../ui/toolkit.h" @@ -116,7 +120,7 @@ char* ui_configfile(char *name) { static int ui_mkdir(char *path) { #ifdef _WIN32 - return mkdir(path); + return _mkdir(path); #else return mkdir(path, S_IRWXU); #endif diff --git a/ui/common/threadpool.c b/ui/common/threadpool.c index d3725c9..7a37407 100644 --- a/ui/common/threadpool.c +++ b/ui/common/threadpool.c @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifndef _WIN32 + #include "threadpool.h" #include "context.h" -#ifndef _WIN32 - #include #include #include diff --git a/ui/common/types.c b/ui/common/types.c index 30cbd03..a704b85 100644 --- a/ui/common/types.c +++ b/ui/common/types.c @@ -162,7 +162,7 @@ void ui_list_clear(UiList *list) { UIEXPORT void ui_list_update(UiList *list) { if(list->update) { - list->update(list, 0); + list->update(list, -1); } } @@ -306,7 +306,9 @@ UIEXPORT UiGeneric* ui_generic_new(UiContext *ctx, char *name) { void ui_int_set(UiInteger* i, int64_t value) { if (i) { if (i->set) { + ui_setop_enable(TRUE); i->set(i, value); + ui_setop_enable(FALSE); } else { i->value = value; } @@ -324,7 +326,9 @@ int64_t ui_int_get(UiInteger* i) { void ui_double_set(UiDouble* d, double value) { if (d) { if (d->set) { + ui_setop_enable(TRUE); d->set(d, value); + ui_setop_enable(FALSE); } else { d->value = value; } @@ -343,7 +347,9 @@ double ui_double_get(UiDouble* d) { void ui_string_set(UiString* s, const char* value) { if (s) { if (s->set) { + ui_setop_enable(TRUE); s->set(s, value); + ui_setop_enable(FALSE); } else { if(s->value.free) { s->value.free(s->value.ptr); @@ -371,7 +377,9 @@ char* ui_string_get(UiString* s) { void ui_text_set(UiText* s, const char* value) { if (s) { if (s->set) { + ui_setop_enable(TRUE); s->set(s, value); + ui_setop_enable(FALSE); } else { if(s->value.free) { s->value.free(s->value.ptr); @@ -426,9 +434,12 @@ void uic_text_copy(UiText *from, UiText *to) { to->selection = from->selection; to->length = from->length; to->remove = from->remove; + to->restore = from->restore; + to->save = from->save; + to->destroy = from->destroy; to->obj = from->obj; - // do not copy the undo manager + // do not copy the undo manager, data1, data2 } void uic_range_copy(UiRange *from, UiRange *to) { @@ -441,6 +452,8 @@ void uic_range_copy(UiRange *from, UiRange *to) { void uic_list_copy(UiList *from, UiList *to) { to->update = from->update; + to->getselection = from->getselection; + to->setselection = from->setselection; to->obj = from->obj; } @@ -468,7 +481,7 @@ void uic_string_save(UiString *s) { void uic_text_save(UiText *t) { if(!t->obj) return; - t->get(t); + t->save(t); t->position(t); } @@ -512,7 +525,6 @@ void uic_text_unbind(UiText *t) { t->length = NULL; t->remove = NULL; t->obj = NULL; - t->undomgr = NULL; } void uic_range_unbind(UiRange *r) { @@ -614,3 +626,13 @@ void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObser destr->observer = observer; cxMempoolSetDestructor(destr, (cx_destructor_func)observer_destructor); } + +static int ui_set_op = 0; + +void ui_setop_enable(int set) { + ui_set_op = set; +} + +int ui_get_setop(void) { + return ui_set_op; +} diff --git a/ui/gtk/button.c b/ui/gtk/button.c index 5123e27..b26a130 100644 --- a/ui/gtk/button.c +++ b/ui/gtk/button.c @@ -114,6 +114,7 @@ void ui_button_clicked(GtkWidget *widget, UiEventData *event) { e.document = event->obj->ctx->document; e.eventdata = NULL; e.intval = event->value; + e.set = ui_get_setop(); event->callback(&e, event->userdata); } @@ -136,7 +137,8 @@ void ui_toggled_obs(void *widget, UiVarEventData *event) { e.window = event->obj->window; e.document = event->obj->ctx->document; e.eventdata = event->var->value; - e.intval = i->get(i); + e.intval = i->get(i); + e.set = ui_get_setop(); ui_notify_evt(i->observers, &e); } @@ -148,6 +150,7 @@ static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) { e.document = event->obj->ctx->document; e.eventdata = NULL; e.intval = gtk_toggle_button_get_active(widget); + e.set = ui_get_setop(); event->callback(&e, event->userdata); } @@ -275,7 +278,7 @@ static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleAr UiObject* current = uic_current_obj(obj); ui_setup_togglebutton( - current, + obj, widget, args.label, args.icon, @@ -318,6 +321,7 @@ static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) { e.document = event->obj->ctx->document; e.eventdata = NULL; e.intval = gtk_check_button_get_active(widget); + e.set = ui_get_setop(); event->callback(&e, event->userdata); } @@ -388,6 +392,7 @@ static void radiobutton_toggled(void *widget, UiEventData *event) { e.document = event->obj->ctx->document; e.eventdata = NULL; e.intval = RADIOBUTTON_GET_ACTIVE(widget); + e.set = ui_get_setop(); event->callback(&e, event->userdata); } diff --git a/ui/gtk/container.c b/ui/gtk/container.c index 8a16977..41c7924 100644 --- a/ui/gtk/container.c +++ b/ui/gtk/container.c @@ -52,17 +52,6 @@ int ui_container_finish(UiObject *obj) { return 1; } -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_LAYOUT1(current, args); - current->container->add(current->container, widget, FALSE); - - return widget; -} - GtkWidget* ui_gtk_vbox_new(int spacing) { #if GTK_MAJOR_VERSION >= 3 return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing); @@ -1041,9 +1030,11 @@ static UIWIDGET splitpane_create(UiObject *obj, UiOrientation orientation, UiSpl 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); + newobj->container = ui_splitpane_container(obj, pane0, orientation, max, args.initial_position); uic_obj_add(obj, newobj); + g_object_set_data(G_OBJECT(pane0), "ui_splitpane", newobj->container); + return pane0; } @@ -1055,13 +1046,15 @@ 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) { +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; ct->current_pane = pane; ct->orientation = orientation; ct->max = max; + ct->initial_position = init; + ct->children = cxArrayListCreateSimple(CX_STORE_POINTERS, 4); return (UiContainer*)ct; } @@ -1073,17 +1066,22 @@ void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) return; } + cxListAdd(s->children, widget); + if(s->pos == 0) { - gtk_paned_set_start_child(GTK_PANED(s->current_pane), widget); + PANED_SET_CHILD1(s->current_pane, widget); + if(s->initial_position > 0) { + gtk_paned_set_position(GTK_PANED(s->current_pane), s->initial_position); + } s->pos++; s->nchildren++; } else { if(s->nchildren+1 == s->max) { - gtk_paned_set_end_child(GTK_PANED(s->current_pane), widget); + PANED_SET_CHILD2(s->current_pane, widget); } else { GtkWidget *pane = create_paned(s->orientation); - gtk_paned_set_start_child(GTK_PANED(pane), widget); - gtk_paned_set_end_child(GTK_PANED(s->current_pane), pane); + PANED_SET_CHILD1(pane, widget); + PANED_SET_CHILD2(s->current_pane, pane); s->current_pane = pane; } @@ -1092,6 +1090,19 @@ void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) } } +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) { + fprintf(stderr, "UI Error: not a splitpane\n"); + return; + } + + GtkWidget *w = cxListAt(ct->children, child_index); + if(w) { + gtk_widget_set_visible(w, visible); + } +} + /* -------------------- ItemList Container -------------------- */ static void remove_item(void *data, void *item) { @@ -1164,6 +1175,10 @@ static void update_itemlist(UiList *list, int c) { elm = list->next(list); index++; } + +#if GTK_MAJOR_VERSION < 4 + gtk_widget_show_all(ct->widget); +#endif } static void destroy_itemlist_container(GtkWidget *w, UiGtkItemListContainer *container) { diff --git a/ui/gtk/container.h b/ui/gtk/container.h index 421451d..b39a3e4 100644 --- a/ui/gtk/container.h +++ b/ui/gtk/container.h @@ -36,11 +36,13 @@ #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif - + #define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout)) #define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE) #define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE) @@ -49,15 +51,14 @@ typedef void (*ui_container_add_f)(UiContainer*, GtkWidget*, UiBool); typedef struct UiDocumentView UiDocumentView; -typedef struct UiLayout UiLayout; -typedef enum UiLayoutBool UiLayoutBool; -enum UiLayoutBool { +typedef enum UiLayoutBool { UI_LAYOUT_UNDEFINED = 0, UI_LAYOUT_TRUE, UI_LAYOUT_FALSE, -}; +} UiLayoutBool; +typedef struct UiLayout UiLayout; struct UiLayout { UiLayoutBool fill; UiBool newline; @@ -127,10 +128,12 @@ typedef struct UiGtkTabView { typedef struct UiSplitPaneContainer { UiContainer container; GtkWidget *current_pane; + CxList *children; UiOrientation orientation; int pos; int max; int nchildren; + int initial_position; } UiSplitPaneContainer; typedef struct UiHeaderbarContainer { @@ -197,7 +200,7 @@ void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview); void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); -UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max); +UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max, int init); void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); diff --git a/ui/gtk/image.c b/ui/gtk/image.c index 3c2a0e6..ea2f6bb 100644 --- a/ui/gtk/image.c +++ b/ui/gtk/image.c @@ -33,59 +33,265 @@ #include "../common/context.h" #include "../common/object.h" +static void imageviewer_destroy(UiImageViewer *iv) { + if(iv->pixbuf) { + g_object_unref(iv->pixbuf); + } + free(iv); +} + +#if GTK_MAJOR_VERSION >= 4 + +static void imageviewer_draw( + GtkDrawingArea *drawingarea, + cairo_t *cr, + int width, + int height, + gpointer userdata) +{ + ui_cairo_draw_image(userdata, cr, width, height); +} -UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args) { - UiObject *current = uic_current_obj(obj); - - GtkWidget *scrolledwindow = SCROLLEDWINDOW_NEW(); -#if GTK_CHECK_VERSION(4, 0, 0) - GtkWidget *image = gtk_picture_new(); #else - GtkWidget *image = gtk_image_new(); + +static gboolean imageviewer_draw(GtkWidget *widget, cairo_t *cr, gpointer userdata) { + int width = gtk_widget_get_allocated_width(widget); + int height = gtk_widget_get_allocated_height(widget); + ui_cairo_draw_image(userdata, cr, width, height); + return FALSE; +} + #endif + +UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args) { + UiObject *current = uic_current_obj(obj); - ui_set_name_and_style(image, args.name, args.style_class); + GtkWidget *drawingarea = gtk_drawing_area_new(); + GtkWidget *toplevel; + GtkWidget *widget = drawingarea; + + gtk_widget_set_size_request(drawingarea, 100, 100); #if GTK_MAJOR_VERSION < 4 GtkWidget *eventbox = gtk_event_box_new(); - SCROLLEDWINDOW_SET_CHILD(scrolledwindow, eventbox); - gtk_container_add(GTK_CONTAINER(eventbox), image); -#else - SCROLLEDWINDOW_SET_CHILD(scrolledwindow, image); - GtkWidget *eventbox = image; + gtk_container_add(GTK_CONTAINER(eventbox), drawingarea); + widget = eventbox; #endif - UI_APPLY_LAYOUT1(current, args); - current->container->add(current->container, scrolledwindow, TRUE); + if(args.scrollarea) { + toplevel = SCROLLEDWINDOW_NEW(); + SCROLLEDWINDOW_SET_CHILD(toplevel, widget); + args.adjustwidgetsize = TRUE; + } else { + toplevel = widget; + } + + UiImageViewer *imgviewer = malloc(sizeof(UiImageViewer)); + memset(imgviewer, 0, sizeof(UiImageViewer)); + imgviewer->obj = obj; + imgviewer->onbuttonpress = args.onbuttonpress; + imgviewer->onbuttonpressdata = args.onbuttonpressdata; + imgviewer->onbuttonrelease = args.onbuttonrelease; + imgviewer->onbuttonreleasedata = args.onbuttonreleasedata; + if(args.image_padding > 0) { + imgviewer->padding_left = args.image_padding; + imgviewer->padding_right = args.image_padding; + imgviewer->padding_top = args.image_padding; + imgviewer->padding_bottom = args.image_padding; + } else { + imgviewer->padding_left = args.image_padding_left; + imgviewer->padding_right = args.image_padding_right; + imgviewer->padding_top = args.image_padding_top; + imgviewer->padding_bottom = args.image_padding_bottom; + } + imgviewer->adjustwidgetsize = args.adjustwidgetsize; + imgviewer->autoscale = args.autoscale; + imgviewer->useradjustable = args.useradjustable; + imgviewer->zoom_scale = 20; + + 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); + imgviewer->var = var; + imgviewer->widget = drawingarea; + if(var) { UiGeneric *value = var->value; value->get = ui_imageviewer_get; value->get_type = ui_imageviewer_get_type; value->set = ui_imageviewer_set; - value->obj = image; + value->obj = imgviewer; if(value->value && value->type && !strcmp(value->type, UI_IMAGE_OBJECT_TYPE)) { GdkPixbuf *pixbuf = value->value; value->value = NULL; ui_imageviewer_set(value, pixbuf, UI_IMAGE_OBJECT_TYPE); + g_object_unref(pixbuf); } } +#if GTK_MAJOR_VERSION >= 4 + gtk_drawing_area_set_draw_func( + GTK_DRAWING_AREA(drawingarea), + imageviewer_draw, + imgviewer, + NULL); + + if(args.useradjustable) { + gtk_widget_set_focusable(drawingarea, TRUE); + } + + GtkEventController *scrollcontroller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_VERTICAL); + g_signal_connect(scrollcontroller, "scroll", G_CALLBACK(ui_imageviewer_scroll), imgviewer); + gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(scrollcontroller)); + + GtkGesture *drag = gtk_gesture_drag_new(); + g_signal_connect(drag, "drag-begin", G_CALLBACK(ui_imageviewer_drag_begin_cb), imgviewer); + g_signal_connect(drag, "drag-end", G_CALLBACK(ui_imageviewer_drag_end_cb), imgviewer); + g_signal_connect(drag, "drag-update", G_CALLBACK(ui_imageviewer_drag_update_cb), imgviewer); + gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(drag)); + + GtkGesture *click = gtk_gesture_click_new(); + g_signal_connect(click, "pressed", G_CALLBACK(ui_imageviewer_pressed_cb), imgviewer); + g_signal_connect(click, "released", G_CALLBACK(ui_imageviewer_released_cb), imgviewer); + gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(click)); + +#elif GTK_MAJOR_VERSION == 3 + g_signal_connect( + drawingarea, + "draw", + G_CALLBACK(imageviewer_draw), + imgviewer); + + gtk_widget_add_events(eventbox, GDK_SCROLL_MASK); + + g_signal_connect( + eventbox, + "scroll-event", + G_CALLBACK(ui_imageviewer_scroll_event), + imgviewer); + g_signal_connect( + eventbox, + "button-press-event", + G_CALLBACK(ui_imageviewer_button_press_event), + imgviewer); + g_signal_connect( + eventbox, + "button-release-event", + G_CALLBACK(ui_imageviewer_button_release_event), + imgviewer); + +#endif + if(args.contextmenu) { - UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, eventbox); - ui_widget_set_contextmenu(eventbox, menu); + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, widget); + ui_widget_set_contextmenu(widget, menu); } + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, toplevel, TRUE); - return scrolledwindow; + return toplevel; +} + +static void imageviewer_reset(UiImageViewer *imgviewer) { + imgviewer->isautoscaled = FALSE; + imgviewer->transx = 0; + imgviewer->transy; + imgviewer->begin_transx = 0; + imgviewer->begin_transy = 0; + imgviewer->scale = 1; + imgviewer->user_scale = 1; +} + +UIWIDGET ui_imageviewer_reset(UIWIDGET w) { + UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer"); + if(imgviewer) { + imageviewer_reset(imgviewer); + gtk_widget_queue_draw(w); + } +} + +UIWIDGET ui_imageviewer_set_autoscale(UIWIDGET w, UiBool set) { + UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer"); + if(imgviewer) { + imgviewer->autoscale = set; + } +} + +UIWIDGET ui_imageviewer_set_adjustwidgetsize(UIWIDGET w, UiBool set) { + UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer"); + if(imgviewer) { + imgviewer->adjustwidgetsize = set; + } +} + +UIWIDGET ui_imageviewer_set_useradjustable(UIWIDGET w, UiBool set) { + UiImageViewer *imgviewer = g_object_get_data(G_OBJECT(w), "uiimageviewer"); + if(imgviewer) { + imgviewer->useradjustable = set; + } +} + +void ui_cairo_draw_image(UiImageViewer *imgviewer, cairo_t *cr, int width, int height) { + if(!imgviewer->pixbuf) { + return; + } + + GdkPixbuf *pixbuf = imgviewer->pixbuf; + double dpixwidth = (double)gdk_pixbuf_get_width(pixbuf); + double dpixheight = (double)gdk_pixbuf_get_height(pixbuf); + + double dwidth = width; + double dheight = height; + double scale = 1; + // if autoscale is enabled, scale the image to fill available space + // if useradjustable is also enabled, the autoscaling is only done once + if(imgviewer->autoscale && imgviewer->scale != 0) { + if(!imgviewer->isautoscaled) { + scale = dwidth / dpixwidth; + if(dpixheight * scale > dheight) { + scale = dheight / dpixheight; + } + + if(imgviewer->useradjustable) { + imgviewer->isautoscaled = TRUE; + } + + imgviewer->scale = scale; + } else { + scale = imgviewer->scale; + } + + imgviewer->user_scale = scale; + } else { + // user-adjusted scaling + //scale = 1 + ((double)imgviewer->zoom / (double)imgviewer->zoom_scale); + scale = imgviewer->user_scale; + } + + dpixwidth *= scale; + dpixheight *= scale; + double x = (dwidth - dpixwidth) / 2; + double y = (dheight - dpixheight) / 2; + + x += imgviewer->transx; + y += imgviewer->transy; + + cairo_translate(cr, x, y); + cairo_scale(cr, scale, scale); + + gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); + cairo_paint(cr); } void* ui_imageviewer_get(UiGeneric *g) { + UiImageViewer *imgviewer = g->obj; + g->value = imgviewer->pixbuf; return g->value; } const char* ui_imageviewer_get_type(UiGeneric *g) { - + return UI_IMAGE_OBJECT_TYPE; } int ui_imageviewer_set(UiGeneric *g, void *value, const char *type) { @@ -93,25 +299,25 @@ int ui_imageviewer_set(UiGeneric *g, void *value, const char *type) { return 1; } - // TODO: do we need to free the previous value here? - - g->value = value; - g->type = type; GdkPixbuf *pixbuf = value; + g_object_ref(pixbuf); + + UiImageViewer *imgviewer = g->obj; + g->value = pixbuf; + + imageviewer_reset(imgviewer); + + if(imgviewer->pixbuf) { + g_object_unref(imgviewer->pixbuf); + } + imgviewer->pixbuf = pixbuf; - if(pixbuf) { + if(imgviewer->adjustwidgetsize && !imgviewer->autoscale) { int width = gdk_pixbuf_get_width(pixbuf); int height = gdk_pixbuf_get_height(pixbuf); - -#if GTK_CHECK_VERSION(4, 0, 0) - GdkTexture *texture = gdk_texture_new_for_pixbuf(pixbuf); - gtk_picture_set_paintable(GTK_PICTURE(g->obj), GDK_PAINTABLE(texture)); -#else - gtk_image_set_from_pixbuf(GTK_IMAGE(g->obj), pixbuf); -#endif - gtk_widget_set_size_request(g->obj, width, height); + gtk_widget_set_size_request(imgviewer->widget, width, height); } - + gtk_widget_queue_draw(imgviewer->widget); return 0; } @@ -127,9 +333,169 @@ int ui_image_load_file(UiGeneric *obj, const char *path) { if(obj->set) { obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE); + g_object_unref(pixbuf); } else { obj->value = pixbuf; + obj->type = UI_IMAGE_OBJECT_TYPE; + } + + return 0; +} + +UIEXPORT int ui_image_load_data(UiGeneric *obj, const void *imgdata, size_t size) { + GBytes *bytes = g_bytes_new_static(imgdata, size); + GInputStream *in = g_memory_input_stream_new_from_bytes(bytes); + GError *error = NULL; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_stream(in, NULL, &error); + g_object_unref(in); + if(!pixbuf) { + return 1; } + if(obj->set) { + obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE); + g_object_unref(pixbuf); + } else { + obj->value = pixbuf; + obj->type = UI_IMAGE_OBJECT_TYPE; + } + return 0; } + +void ui_image_ref(UIIMAGE img) { + g_object_ref(img); +} + +void ui_image_unref(UIIMAGE img) { + g_object_unref(img); +} + +#if GTK_MAJOR_VERSION >= 4 + +gboolean ui_imageviewer_scroll( + GtkEventControllerScroll *widget, + gdouble dx, + gdouble dy, + gpointer userdata) +{ + UiImageViewer *imgviewer = userdata; + if(imgviewer->useradjustable) { + double step = dy / imgviewer->zoom_scale; + if(imgviewer->user_scale - step > 0) { + imgviewer->user_scale -= step; + } + + imgviewer->scale = 0; // disable autoscale + gtk_widget_queue_draw(imgviewer->widget); + return TRUE; + } + return FALSE; +} + +void ui_imageviewer_drag_begin_cb( + GtkGestureDrag *self, + gdouble start_x, + gdouble start_y, + gpointer userdata) +{ + UiImageViewer *imgviewer = userdata; + imgviewer->begin_transx = imgviewer->transx; + imgviewer->begin_transy = imgviewer->transy; +} + +void ui_imageviewer_drag_end_cb( + GtkGestureDrag* self, + gdouble x, + gdouble y, + gpointer userdata) +{ + +} + +void ui_imageviewer_drag_update_cb( + GtkGestureDrag *self, + gdouble x, + gdouble y, + gpointer userdata) +{ + UiImageViewer *imgviewer = userdata; + if(imgviewer->useradjustable) { + imgviewer->transx = imgviewer->begin_transx + x; + imgviewer->transy = imgviewer->begin_transy + y; + gtk_widget_queue_draw(imgviewer->widget); + } +} + +static void imgviewer_button_event( + GtkGestureClick *gesture, + UiImageViewer *imgviewer, + ui_callback callback, + void *userdata) +{ + UiEvent event; + event.obj = imgviewer->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.intval = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(gesture)); + event.set = 0; + callback(&event, userdata); +} + +void ui_imageviewer_pressed_cb( + GtkGestureClick *self, + gint n_press, + gdouble x, + gdouble y, + gpointer userdata) +{ + UiImageViewer *imgviewer = userdata; + if(imgviewer->onbuttonpress) { + imgviewer_button_event(self, imgviewer, imgviewer->onbuttonpress, imgviewer->onbuttonpressdata); + } +} + +void ui_imageviewer_released_cb( + GtkGestureClick *self, + gint n_press, + gdouble x, + gdouble y, + gpointer userdata) +{ + UiImageViewer *imgviewer = userdata; + if(imgviewer->onbuttonrelease) { + imgviewer_button_event(self, imgviewer, imgviewer->onbuttonrelease, imgviewer->onbuttonreleasedata); + } +} + +#else + +gboolean ui_imageviewer_scroll_event( + GtkWidget *widget, + GdkEventScroll event, + gpointer userdata) +{ + // TODO + return FALSE; +} + +gboolean ui_imageviewer_button_press_event( + GtkWidget *widget, + GdkEventButton event, + gpointer userdata) +{ + // TODO + return FALSE; +} + +gboolean ui_imageviewer_button_release_event( + GtkWidget *widget, + GdkEventButton event, + gpointer userdata) +{ + // TODO + return FALSE; +} + +#endif diff --git a/ui/gtk/image.h b/ui/gtk/image.h index 00c205d..987c73b 100644 --- a/ui/gtk/image.h +++ b/ui/gtk/image.h @@ -36,11 +36,99 @@ extern "C" { #endif +typedef struct UiImageViewer { + UiObject *obj; + GtkWidget *widget; + UiVar *var; + int padding_left; + int padding_right; + int padding_top; + int padding_bottom; + UiBool autoscale; + UiBool adjustwidgetsize; + UiBool useradjustable; + GdkPixbuf *pixbuf; + + double zoom_scale; + int transx; + int transy; + int begin_transx; + int begin_transy; + UiBool isautoscaled; + double user_scale; + double scale; + + ui_callback onbuttonpress; + void *onbuttonpressdata; + ui_callback onbuttonrelease; + void *onbuttonreleasedata; +} UiImageViewer; + +void ui_cairo_draw_image(UiImageViewer *imgviewer, cairo_t *cr, int width, int height); void* ui_imageviewer_get(UiGeneric *g); const char* ui_imageviewer_get_type(UiGeneric *g); int ui_imageviewer_set(UiGeneric *g, void *value, const char *type); +#if GTK_MAJOR_VERSION >= 4 + +gboolean ui_imageviewer_scroll( + GtkEventControllerScroll *widget, + gdouble dx, + gdouble dy, + gpointer userdata); + +void ui_imageviewer_drag_begin_cb( + GtkGestureDrag* self, + gdouble start_x, + gdouble start_y, + gpointer userdata); + +void ui_imageviewer_drag_end_cb( + GtkGestureDrag* self, + gdouble x, + gdouble y, + gpointer userdata); + +void ui_imageviewer_drag_update_cb( + GtkGestureDrag* self, + gdouble x, + gdouble y, + gpointer userdata); + +void ui_imageviewer_pressed_cb( + GtkGestureClick *self, + gint n_press, + gdouble x, + gdouble y, + gpointer userdata); + +void ui_imageviewer_released_cb( + GtkGestureClick *self, + gint n_press, + gdouble x, + gdouble y, + gpointer userdata); + +#else + +gboolean ui_imageviewer_scroll_event( + GtkWidget *widget, + GdkEventScroll event, + gpointer userdata); + +gboolean ui_imageviewer_button_press_event( + GtkWidget *widget, + GdkEventButton event, + gpointer userdata); + +gboolean ui_imageviewer_button_release_event( + GtkWidget *widget, + GdkEventButton event, + gpointer userdata); + +#endif + #ifdef __cplusplus } #endif diff --git a/ui/gtk/list.c b/ui/gtk/list.c index aac751f..978ab53 100644 --- a/ui/gtk/list.c +++ b/ui/gtk/list.c @@ -57,6 +57,14 @@ static GtkTargetEntry targetentries[] = }; */ +static void listview_copy_static_elements(UiListView *listview, char **elm, size_t nelm) { + listview->elements = calloc(nelm, sizeof(char*)); + listview->nelm = nelm; + for(int i=0;ielements[i] = strdup(elm[i]); + } +} + #if GTK_CHECK_VERSION(4, 10, 0) @@ -248,6 +256,10 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) { list->setselection = ui_listview_setselection2; 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 + ui_update_liststore_static(ls, listview->elements, listview->nelm); } // event handling @@ -257,6 +269,10 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) { // is ignored g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), listview); } + if(args.contextmenu) { + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, view); + ui_widget_set_contextmenu(view, menu); + } // add widget to parent GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); @@ -323,11 +339,15 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { list->setselection = ui_combobox_setselection; 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 + ui_update_liststore_static(ls, listview->elements, listview->nelm); } // event handling if(args.onactivate) { - g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), listview); + g_signal_connect(view, "notify::selected", G_CALLBACK(ui_dropdown_notify), listview); } // add widget to parent @@ -336,6 +356,15 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { return view; } +void ui_listview_select(UIWIDGET listview, int index) { + GtkSelectionModel *model = gtk_list_view_get_model(GTK_LIST_VIEW(listview)); + gtk_selection_model_select_item(model, index, TRUE); +} + +void ui_combobox_select(UIWIDGET dropdown, int index) { + gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index); +} + UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { UiObject* current = uic_current_obj(obj); @@ -413,6 +442,10 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { if(args.onactivate) { g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), tableview); } + if(args.contextmenu) { + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, view); + ui_widget_set_contextmenu(view, menu); + } // add widget to parent GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); @@ -449,18 +482,13 @@ static void listview_event(ui_callback cb, void *cbdata, UiListView *view) { event.window = event.obj->window; event.intval = view->selection.count; event.eventdata = &view->selection; + event.set = ui_get_setop(); if(cb) { cb(&event, cbdata); } } -void ui_columnview_activate(void *ignore, guint position, gpointer userdata) { - UiListView *view = userdata; - listview_event(view->onactivate, view->onactivatedata, view); -} - -void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer userdata) { - UiListView *view = userdata; +static void listview_update_selection(UiListView *view) { free(view->selection.rows); view->selection.count = 0; view->selection.rows = NULL; @@ -483,7 +511,37 @@ void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guin } else { free(newselection); } +} + +void ui_dropdown_notify(GtkWidget *dropdown, GObject *pspec, gpointer userdata) { + UiListView *view = userdata; + guint index = gtk_drop_down_get_selected(GTK_DROP_DOWN(dropdown)); + GObject *item = gtk_drop_down_get_selected_item(GTK_DROP_DOWN(dropdown)); + if(item && view->onactivate) { + ObjWrapper *eventdata = (ObjWrapper*)item; + UiEvent event; + event.obj = view->obj; + event.document = event.obj->ctx->document; + event.window = event.obj->window; + event.intval = index; + event.eventdata = eventdata->data; + event.set = ui_get_setop(); + view->onactivate(&event, view->onactivatedata); + } +} + +void ui_columnview_activate(void *ignore, guint position, gpointer userdata) { + UiListView *view = userdata; + if(view->selection.count == 0) { + listview_update_selection(view); + } + listview_event(view->onactivate, view->onactivatedata, view); +} + +void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer userdata) { + UiListView *view = userdata; + listview_update_selection(view); listview_event(view->onselection, view->onselectiondata, view); } @@ -504,6 +562,7 @@ void ui_dropdown_activate(GtkDropDown* self, gpointer userdata) { event.window = event.obj->window; event.intval = view->selection.count; event.eventdata = &view->selection; + event.set = ui_get_setop(); view->onactivate(&event, view->onactivatedata); } } @@ -518,9 +577,32 @@ void ui_update_liststore(GListStore *liststore, UiList *list) { } } +void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm) { + g_list_store_remove_all(liststore); + for(int i=0;iobj; - ui_update_liststore(view->liststore, list); + if(i < 0) { + ui_update_liststore(view->liststore, list); + } else { + void *value = list->get(list, i); + if(value) { + ObjWrapper *obj = obj_wrapper_new(value); + // 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); + } + } + } } UiListSelection ui_listview_getselection2(UiList *list) { @@ -533,6 +615,7 @@ UiListSelection ui_listview_getselection2(UiList *list) { } void ui_listview_setselection2(UiList *list, UiListSelection selection) { + ui_setop_enable(TRUE); UiListView *view = list->obj; UiListSelection newselection; newselection.count = view->selection.count; @@ -551,6 +634,7 @@ void ui_listview_setselection2(UiList *list, UiListSelection selection) { gtk_selection_model_select_item(view->selectionmodel, selection.rows[i], FALSE); } } + ui_setop_enable(FALSE); } UiListSelection ui_combobox_getselection(UiList *list) { @@ -566,16 +650,98 @@ UiListSelection ui_combobox_getselection(UiList *list) { } void ui_combobox_setselection(UiList *list, UiListSelection selection) { + ui_setop_enable(TRUE); UiListView *view = list->obj; if(selection.count > 0) { gtk_drop_down_set_selected(GTK_DROP_DOWN(view->widget), selection.rows[0]); } else { gtk_drop_down_set_selected(GTK_DROP_DOWN(view->widget), GTK_INVALID_LIST_POSITION); } + ui_setop_enable(FALSE); } #else +static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *model, void *elm) { + // set column values + int c = 0; + for(int i=0;icolumns;i++,c++) { + void *data = model->getvalue(elm, c); + + GValue value = G_VALUE_INIT; + switch(model->types[i]) { + case UI_STRING: + case UI_STRING_FREE: { + g_value_init(&value, G_TYPE_STRING); + g_value_set_string(&value, data); + if(model->types[i] == UI_STRING_FREE) { + free(data); + } + break; + } + case UI_INTEGER: { + g_value_init(&value, G_TYPE_INT); + intptr_t intptr = (intptr_t)data; + g_value_set_int(&value, (int)intptr); + break; + } + case UI_ICON: { + g_value_init(&value, G_TYPE_OBJECT); + UiIcon *icon = data; +#if GTK_MAJOR_VERSION >= 4 + g_value_set_object(&value, icon->info); // TODO: does this work? +#else + if(!icon->pixbuf && icon->info) { + GError *error = NULL; + GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); + icon->pixbuf = pixbuf; + } + + if(icon->pixbuf) { + g_value_set_object(&value, icon->pixbuf); + } +#endif + break; + } + case UI_ICON_TEXT: + case UI_ICON_TEXT_FREE: { + UiIcon *icon = data; +#if GTK_MAJOR_VERSION >= 4 + if(icon) { + GValue iconvalue = G_VALUE_INIT; + g_value_init(&iconvalue, G_TYPE_OBJECT); + g_value_set_object(&iconvalue, ui_icon_pixbuf(icon)); + gtk_list_store_set_value(store, &iter, c, &iconvalue); + } +#else + GValue pixbufvalue = G_VALUE_INIT; + if(icon) { + if(!icon->pixbuf && icon->info) { + GError *error = NULL; + GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); + icon->pixbuf = pixbuf; + } + g_value_init(&pixbufvalue, G_TYPE_OBJECT); + g_value_set_object(&pixbufvalue, icon->pixbuf); + gtk_list_store_set_value(store, iter, c, &pixbufvalue); + } +#endif + c++; + + char *str = model->getvalue(elm, c); + g_value_init(&value, G_TYPE_STRING); + g_value_set_string(&value, str); + if(model->types[i] == UI_ICON_TEXT_FREE) { + free(str); + } + break; + } + } + + gtk_list_store_set_value(store, iter, c, &value); + } +} + static GtkListStore* create_list_store(UiList *list, UiModel *model) { int columns = model->columns; GType types[2*columns]; @@ -603,83 +769,7 @@ static GtkListStore* create_list_store(UiList *list, UiModel *model) { GtkTreeIter iter; gtk_list_store_insert (store, &iter, -1); - // set column values - int c = 0; - for(int i=0;igetvalue(elm, c); - - GValue value = G_VALUE_INIT; - switch(model->types[i]) { - case UI_STRING: - case UI_STRING_FREE: { - g_value_init(&value, G_TYPE_STRING); - g_value_set_string(&value, data); - if(model->types[i] == UI_STRING_FREE) { - free(data); - } - break; - } - case UI_INTEGER: { - g_value_init(&value, G_TYPE_INT); - intptr_t intptr = (intptr_t)data; - g_value_set_int(&value, (int)intptr); - break; - } - case UI_ICON: { - g_value_init(&value, G_TYPE_OBJECT); - UiIcon *icon = data; -#if GTK_MAJOR_VERSION >= 4 - g_value_set_object(&value, icon->info); // TODO: does this work? -#else - if(!icon->pixbuf && icon->info) { - GError *error = NULL; - GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); - icon->pixbuf = pixbuf; - } - - if(icon->pixbuf) { - g_value_set_object(&value, icon->pixbuf); - } -#endif - break; - } - case UI_ICON_TEXT: - case UI_ICON_TEXT_FREE: { - UiIcon *icon = data; -#if GTK_MAJOR_VERSION >= 4 - if(icon) { - GValue iconvalue = G_VALUE_INIT; - g_value_init(&iconvalue, G_TYPE_OBJECT); - g_value_set_object(&iconvalue, ui_icon_pixbuf(icon)); - gtk_list_store_set_value(store, &iter, c, &iconvalue); - } -#else - GValue pixbufvalue = G_VALUE_INIT; - if(icon) { - if(!icon->pixbuf && icon->info) { - GError *error = NULL; - GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); - icon->pixbuf = pixbuf; - } - g_value_init(&pixbufvalue, G_TYPE_OBJECT); - g_value_set_object(&pixbufvalue, icon->pixbuf); - gtk_list_store_set_value(store, &iter, c, &pixbufvalue); - } -#endif - c++; - - char *str = model->getvalue(elm, c); - g_value_init(&value, G_TYPE_STRING); - g_value_set_string(&value, str); - if(model->types[i] == UI_ICON_TEXT_FREE) { - free(str); - } - break; - } - } - - gtk_list_store_set_value(store, &iter, c, &value); - } + update_list_row(store, &iter, model, elm); // next row elm = list->next(list); @@ -723,6 +813,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) { g_object_unref(listmodel); UiListView *listview = malloc(sizeof(UiListView)); + memset(listview, 0, sizeof(UiListView)); listview->obj = obj; listview->widget = view; listview->var = var; @@ -794,6 +885,17 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) { return scroll_area; } +void ui_listview_select(UIWIDGET listview, int index) { + GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(listview)); + GtkTreePath *path = gtk_tree_path_new_from_indicesv(&index, 1); + gtk_tree_selection_select_path(sel, path); + //g_object_unref(path); +} + +void ui_combobox_select(UIWIDGET dropdown, int index) { + gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index); +} + UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { UiObject* current = uic_current_obj(obj); @@ -870,6 +972,7 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { // 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; tableview->widget = view; tableview->var = var; @@ -963,9 +1066,18 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { void ui_listview_update(UiList *list, int i) { UiListView *view = list->obj; - GtkListStore *store = create_list_store(list, view->model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store)); - g_object_unref(G_OBJECT(store)); + if(i < 0) { + GtkListStore *store = create_list_store(list, view->model); + gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + } else { + void *elm = list->get(list, 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, elm); + } + } } UiListSelection ui_listview_getselection(UiList *list) { @@ -977,11 +1089,13 @@ UiListSelection ui_listview_getselection(UiList *list) { } void ui_listview_setselection(UiList *list, UiListSelection selection) { + ui_setop_enable(TRUE); UiListView *view = list->obj; GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view->widget)); GtkTreePath *path = gtk_tree_path_new_from_indicesv(selection.rows, selection.count); gtk_tree_selection_select_path(sel, path); //g_object_unref(path); + ui_setop_enable(FALSE); } @@ -996,7 +1110,7 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { 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.onactivate, args.onactivatedata); + 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_LAYOUT1(current, args); @@ -1005,16 +1119,29 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { return combobox; } -GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f, void *udata) { +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; UiList *list = var ? var->value : NULL; GtkListStore *listmodel = create_list_store(list, model); + if(!list && elm && nelm > 0) { + listview_copy_static_elements(uicbox, elm, nelm); + for(int i=0;ielements[i]); + gtk_list_store_set_value(listmodel, &iter, 0, &value); + } + } + if(listmodel) { gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(listmodel)); g_object_unref(listmodel); @@ -1054,7 +1181,7 @@ GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_call event->userdata = udata; event->callback = f; event->value = 0; - event->customdata = NULL; + event->customdata = uicbox; g_signal_connect( combobox, @@ -1067,12 +1194,23 @@ GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_call } void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) { + int index = gtk_combo_box_get_active(widget); + UiListView *listview = e->customdata; + void *eventdata = NULL; + if(listview->var && listview->var->value) { + UiList *list = listview->var->value; + eventdata = ui_list_get(list, index); + } else if(listview->elements && listview->nelm > index) { + eventdata = listview->elements[index]; + } + UiEvent event; event.obj = e->obj; event.window = event.obj->window; event.document = event.obj->ctx->document; - event.eventdata = NULL; - event.intval = gtk_combo_box_get_active(widget); + event.eventdata = eventdata; + event.intval = index; + event.set = ui_get_setop(); e->callback(&event, e->userdata); } @@ -1093,10 +1231,12 @@ UiListSelection ui_combobox_getselection(UiList *list) { } void ui_combobox_setselection(UiList *list, UiListSelection selection) { + ui_setop_enable(TRUE); UiListView *combobox = list->obj; if(selection.count > 0) { gtk_combo_box_set_active(GTK_COMBO_BOX(combobox->widget), selection.rows[0]); } + ui_setop_enable(FALSE); } @@ -1118,6 +1258,7 @@ void ui_listview_activate_event( e.document = event->obj->ctx->document; e.eventdata = &selection; e.intval = selection.count > 0 ? selection.rows[0] : -1; + e.set = ui_get_setop(); event->activate(&e, event->activatedata); if(selection.count > 0) { @@ -1137,6 +1278,7 @@ void ui_listview_selection_event( e.document = event->obj->ctx->document; e.eventdata = &selection; e.intval = selection.count > 0 ? selection.rows[0] : -1; + e.set = ui_get_setop(); event->selection(&e, event->selectiondata); if(selection.count > 0) { @@ -1195,6 +1337,7 @@ static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source, double event.document = event.obj->ctx->document; event.eventdata = dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondragstart(&event, listview->ondragstartdata); } @@ -1230,6 +1373,7 @@ static void ui_listview_drag_end(GtkDragSource *self, GdkDrag *drag, gboolean de event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondragcomplete(&event, listview->ondragcompletedata); } } @@ -1258,6 +1402,7 @@ static gboolean ui_listview_drop( event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondrop(&event, listview->ondropdata); } @@ -1320,6 +1465,7 @@ static void ui_listview_drag_getdata( event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondragstart(&event, listview->ondragstartdata); } } @@ -1344,6 +1490,7 @@ static void ui_listview_drag_end( event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondragcomplete(&event, listview->ondragcompletedata); } } @@ -1389,6 +1536,7 @@ static void ui_listview_drag_data_received( event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondrop(&event, listview->ondropdata); } } @@ -1490,7 +1638,15 @@ void ui_table_dragdest_a(UIWIDGET tablewidget, int actions, char **targets, int void ui_listview_destroy(GtkWidget *w, UiListView *v) { //gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL); - ui_destroy_boundvar(v->obj->ctx, v->var); + 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); + } #if GTK_CHECK_VERSION(4, 10, 0) free(v->columns); #endif @@ -1499,7 +1655,15 @@ void ui_listview_destroy(GtkWidget *w, UiListView *v) { } void ui_combobox_destroy(GtkWidget *w, UiListView *v) { - ui_destroy_boundvar(v->obj->ctx, v->var); + 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); } @@ -1654,7 +1818,7 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) { list->obj = uilistbox; list->update = ui_listbox_dynamic_update; - ui_listbox_dynamic_update(list, 0); + ui_listbox_dynamic_update(list, -1); } } @@ -1687,6 +1851,15 @@ void ui_listbox_dynamic_update(UiList *list, int x) { // unbind/free previous list vars CxIterator i = cxListIterator(uilistbox->sublists); cx_foreach(UiListBoxSubList *, s, i) { + // TODO: "unbind/free previous list vars" will also remove + // the widget list. This makes the widget optimization + // in ui_listbox_update_sublist pointless + // Is it actually possible to not recreate the whole list? + CxIterator r = cxListIterator(s->widgets); + cx_foreach(GtkWidget*, widget, r) { + LISTBOX_REMOVE(uilistbox->listbox, widget); + } + if(s->var) { UiList *sl = s->var->value; sl->obj = NULL; @@ -1845,6 +2018,7 @@ void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user event.document = event.obj->ctx->document; event.eventdata = &eventdata; event.intval = data->value0; + event.set = ui_get_setop(); if(data->callback) { data->callback(&event, data->userdata); diff --git a/ui/gtk/list.h b/ui/gtk/list.h index 1ff4029..784e644 100644 --- a/ui/gtk/list.h +++ b/ui/gtk/list.h @@ -45,6 +45,8 @@ typedef struct UiListView { GtkWidget *widget; UiVar *var; UiModel *model; + char **elements; + size_t nelm; #if GTK_CHECK_VERSION(4, 10, 0) GListStore *liststore; GtkSelectionModel *selectionmodel; @@ -100,7 +102,6 @@ struct UiListBox { void *onactivatedata; ui_callback onbuttonclick; void *onbuttonclickdata; - GtkListBoxRow *first_row; }; @@ -108,11 +109,13 @@ struct UiListBox { #if GTK_CHECK_VERSION(4, 10, 0) void ui_update_liststore(GListStore *liststore, UiList *list); +void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm); void ui_listview_update2(UiList *list, int i); UiListSelection ui_listview_getselection2(UiList *list); void ui_listview_setselection2(UiList *list, UiListSelection selection); +void ui_dropdown_notify(GtkWidget *dropdown, GObject *pspec, gpointer userdata); void ui_columnview_activate(void *ignore, guint position, gpointer userdata); void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer user_data); @@ -155,7 +158,7 @@ void ui_listview_add_dnd(UiListView *listview, UiListArgs *args); void ui_listview_enable_drop(UiListView *listview, UiListArgs *args); UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); -GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f, void *udata); +GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata); void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e); void ui_combobox_modelupdate(UiList *list, int i); UiListSelection ui_combobox_getselection(UiList *list); diff --git a/ui/gtk/menu.c b/ui/gtk/menu.c index 085c4db..a54a34b 100644 --- a/ui/gtk/menu.c +++ b/ui/gtk/menu.c @@ -233,6 +233,20 @@ void add_checkitemnv_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject } */ +static void menuitem_list_remove_binding(void *obj) { + UiActiveMenuItemList *ls = obj; + UiList *list = ls->var->value; + CxList *bindings = list->obj; + if(bindings) { + (void)cxListFindRemove(bindings, obj); + if(cxListSize(bindings) == 0) { + cxListFree(bindings); + list->obj = NULL; + list->update = NULL; + } + } +} + void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { UiMenuItemList *il = (UiMenuItemList*)item; const CxAllocator *a = obj->ctx->allocator; @@ -247,21 +261,45 @@ 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); - ls->list = var->value; + //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; + list->update = ui_menulist_update; + list->getselection = NULL; + list->setselection = NULL; + + // It is possible, that the UiVar is from a global shared context, + // used by multiple windows. To support this usecase, the list->obj + // binding object is a list of all connected UiActiveMenuItemList. + CxList *bindings = list->obj; + if(!bindings) { + bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS); + list->obj = bindings; + } + cxListAdd(bindings, ls); + + // The destruction of the toplevel obj must remove the menulist binding + uic_context_add_destructor(obj->ctx, menuitem_list_remove_binding, ls); + + ui_update_menuitem_list(ls); + } ls->callback = il->callback; ls->userdata = il->userdata; - - UiObserver *observer = ui_observer_new((ui_callback)ui_update_menuitem_list, ls); - ls->list->observers = ui_obsvlist_add(ls->list->observers, observer); - uic_list_register_observer_destructor(obj->ctx, ls->list, observer); - - ui_update_menuitem_list(NULL, ls); +} + +void ui_menulist_update(UiList *list, int ignored) { + CxList *bindings = list->obj; + CxIterator i = cxListIterator(bindings); + cx_foreach(UiActiveMenuItemList *, ls, i) { + ui_update_menuitem_list(ls); + } } -void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) { +void ui_update_menuitem_list(UiActiveMenuItemList *list) { // remove old items if(list->oldcount > 0) { int i = 0; @@ -276,7 +314,9 @@ void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) { } } - void* elm = ui_list_first(list->list); + UiList *ls = list->var->value; + + void* elm = ui_list_first(ls); if(elm) { GtkWidget *widget = gtk_separator_menu_item_new(); gtk_menu_shell_insert(list->menu, widget, list->index); @@ -312,7 +352,7 @@ void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) { event); } - elm = ui_list_next(list->list); + elm = ui_list_next(ls); i++; } @@ -506,6 +546,20 @@ void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *ob } +static void menuitem_list_remove_binding(void *obj) { + UiActiveGMenuItemList *ls = obj; + UiList *list = ls->var->value; + CxList *bindings = list->obj; + if(bindings) { + (void)cxListFindRemove(bindings, obj); + if(cxListSize(bindings) == 0) { + cxListFree(bindings); + list->obj = NULL; + list->update = NULL; + } + } +} + void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { UiMenuItemList *il = (UiMenuItemList*)item; @@ -521,17 +575,34 @@ 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(ui_global_context(), il->varname, UI_VAR_LIST); + UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST); ls->var = var; - UiList *list = var->value; + if(var) { + UiList *list = var->value; + list->update = ui_menulist_update; + list->getselection = NULL; + list->setselection = NULL; + + // It is possible, that the UiVar is from a global shared context, + // used by multiple windows. To support this usecase, the list->obj + // binding object is a list of all connected UiActiveMenuItemList. + CxList *bindings = list->obj; + if(!bindings) { + bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS); + list->obj = bindings; + } + cxListAdd(bindings, ls); + + // The destruction of the toplevel obj must remove the menulist binding + uic_context_add_destructor(obj->ctx, menuitem_list_remove_binding, ls); + + ui_update_gmenu_item_list(ls); + } ls->callback = il->callback; ls->userdata = il->userdata; - UiObserver *observer = ui_observer_new((ui_callback)ui_update_gmenu_item_list, ls); - list->observers = ui_obsvlist_add(list->observers, observer); - uic_list_register_observer_destructor(obj->ctx, list, observer); - 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); @@ -554,8 +625,6 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject "destroy", G_CALLBACK(ui_destroy_userdata), event); - - ui_update_gmenu_item_list(NULL, ls); } void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) { @@ -588,7 +657,15 @@ void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* par } -void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list) { +void ui_menulist_update(UiList *list, int ignored) { + CxList *bindings = list->obj; + CxIterator i = cxListIterator(bindings); + cx_foreach(UiActiveGMenuItemList *, ls, i) { + ui_update_gmenu_item_list(ls); + } +} + +void ui_update_gmenu_item_list(UiActiveGMenuItemList *list) { // remove old items for(int i=0;ioldcount;i++) { g_menu_remove(list->menu, list->index); diff --git a/ui/gtk/menu.h b/ui/gtk/menu.h index b8bff03..dc6240c 100644 --- a/ui/gtk/menu.h +++ b/ui/gtk/menu.h @@ -55,10 +55,10 @@ struct UiActiveMenuItemList { GtkMenuShell *menu; int index; int oldcount; - UiList *list; - ui_getvaluefunc getvalue; - ui_callback callback; - void *userdata; + UiVar *var; + ui_getvaluefunc getvalue; + ui_callback callback; + void *userdata; }; void ui_add_menu_items(GtkWidget *parent, int i, UiMenu *menu, UiObject *obj); @@ -72,7 +72,8 @@ void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject * void add_checkitemnv_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj); void add_menuitem_list_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj); -void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list); +void ui_menulist_update(UiList *list, int ignored); +void ui_update_menuitem_list(UiActiveMenuItemList *list); void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event); void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event); int64_t ui_checkitem_get(UiInteger *i); @@ -108,7 +109,8 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event); void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event); -void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list); +void ui_menulist_update(UiList *list, int ignored); +void ui_update_gmenu_item_list(UiActiveGMenuItemList *list); #endif diff --git a/ui/gtk/model.c b/ui/gtk/model.c deleted file mode 100644 index 6db4e61..0000000 --- a/ui/gtk/model.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 -#include - -#include "model.h" -#include "image.h" -#include "toolkit.h" - -#define IS_UI_LIST_MODEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), list_model_type)) -#define UI_LIST_MODEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), list_model_type, UiListModel)) - -static void list_model_class_init(GObjectClass *cl, gpointer data); -static void list_model_interface_init(GtkTreeModelIface *i, gpointer data); -static void list_model_init(UiListModel *instance, GObjectClass *cl); - -static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data); -static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data); - -static GObjectClass list_model_class; -static const GTypeInfo list_model_info = { - sizeof(GObjectClass), - NULL, - NULL, - (GClassInitFunc)list_model_class_init, - NULL, - NULL, - sizeof(UiListModel), - 0, - (GInstanceInitFunc)list_model_init -}; -static const GInterfaceInfo list_model_interface_info = { - (GInterfaceInitFunc)list_model_interface_init, - NULL, - NULL -}; -static GType list_model_type; - -static const GInterfaceInfo list_model_dnd_dest_interface_info = { - (GInterfaceInitFunc)list_model_dnd_dest_interface_init, - NULL, - NULL -}; -static const GInterfaceInfo list_model_dnd_src_interface_info = { - (GInterfaceInitFunc)list_model_dnd_src_interface_init, - NULL, - NULL -}; - -void ui_list_init() { - list_model_type = g_type_register_static( - G_TYPE_OBJECT, - "UiListModel", - &list_model_info, - (GTypeFlags)0); - g_type_add_interface_static( - list_model_type, - GTK_TYPE_TREE_MODEL, - &list_model_interface_info); - g_type_add_interface_static( - list_model_type, - GTK_TYPE_TREE_DRAG_DEST, - &list_model_dnd_dest_interface_info); - g_type_add_interface_static( - list_model_type, - GTK_TYPE_TREE_DRAG_SOURCE, - &list_model_dnd_src_interface_info); -} - -static void list_model_class_init(GObjectClass *cl, gpointer data) { - cl->dispose = ui_list_model_dispose; - cl->finalize = ui_list_model_finalize; - -} - -static void list_model_interface_init(GtkTreeModelIface *i, gpointer data) { - i->get_flags = ui_list_model_get_flags; - i->get_n_columns = ui_list_model_get_n_columns; - i->get_column_type = ui_list_model_get_column_type; - i->get_iter = ui_list_model_get_iter; - i->get_path = ui_list_model_get_path; - i->get_value = ui_list_model_get_value; - i->iter_next = ui_list_model_iter_next; - i->iter_children = ui_list_model_iter_children; - i->iter_has_child = ui_list_model_iter_has_child; - i->iter_n_children = ui_list_model_iter_n_children; - i->iter_nth_child = ui_list_model_iter_nth_child; - i->iter_parent = ui_list_model_iter_parent; -} - -static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data) { - i->drag_data_received = ui_list_model_drag_data_received; - i->row_drop_possible = ui_list_model_row_drop_possible; -} - -static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data) { - i->drag_data_delete = ui_list_model_drag_data_delete; - i->drag_data_get = ui_list_model_drag_data_get; - i->row_draggable = ui_list_model_row_draggable; -} - -static void list_model_init(UiListModel *instance, GObjectClass *cl) { - instance->columntypes = NULL; - instance->var = NULL; - instance->numcolumns = 0; - instance->stamp = g_random_int(); -} - -static GType ui_gtk_type(UiModelType type) { - switch(type) { - default: break; - case UI_STRING: return G_TYPE_STRING; - case UI_INTEGER: return G_TYPE_INT; - } - return G_TYPE_INVALID; -} - -static void ui_model_set_value(GType type, void *data, GValue *value) { - switch(type) { - default: break; - case G_TYPE_OBJECT: { - value->g_type = G_TYPE_OBJECT; - g_value_set_object(value, data); - return; - } - case G_TYPE_STRING: { - value->g_type = G_TYPE_STRING; - g_value_set_string(value, data); - return; - } - case G_TYPE_INT: { - value->g_type = G_TYPE_INT; - int *i = data; - g_value_set_int(value, *i); - return; - } - } - value->g_type = G_TYPE_INVALID; -} - -UiListModel* ui_list_model_new(UiObject *obj, UiVar *var, UiModel *info) { - UiListModel *model = g_object_new(list_model_type, NULL); - model->obj = obj; - model->model = info; - model->var = var; - model->columntypes = calloc(sizeof(GType), 2 * info->columns); - int ncol = 0; - for(int i=0;icolumns;i++) { - UiModelType type = info->types[i]; - if(type == UI_ICON_TEXT) { - model->columntypes[ncol] = G_TYPE_OBJECT; - ncol++; - model->columntypes[ncol] = G_TYPE_STRING; - } else { - model->columntypes[ncol] = ui_gtk_type(info->types[i]); - } - ncol++; - } - model->numcolumns = ncol; - return model; -} - -void ui_list_model_dispose(GObject *obj) { - -} - -void ui_list_model_finalize(GObject *obj) { - -} - - -GtkTreeModelFlags ui_list_model_get_flags(GtkTreeModel *tree_model) { - return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST); -} - -gint ui_list_model_get_n_columns(GtkTreeModel *tree_model) { - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), 0); - UiListModel *model = UI_LIST_MODEL(tree_model); - return model->numcolumns; -} - -GType ui_list_model_get_column_type(GtkTreeModel *tree_model, gint index) { - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), G_TYPE_INVALID); - UiListModel *model = UI_LIST_MODEL(tree_model); - g_return_val_if_fail(index < model->numcolumns, G_TYPE_INVALID); - return model->columntypes[index]; -} - -gboolean ui_list_model_get_iter( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - g_assert(IS_UI_LIST_MODEL(tree_model)); - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - - // check the depth of the path - // a list must have a depth of 1 - gint depth = gtk_tree_path_get_depth(path); - g_assert(depth == 1); - - // get row - gint *indices = gtk_tree_path_get_indices(path); - gint row = indices[0]; - - // check row - if(row == 0) { - // we don't need to count if the first element is requested - if(list->first(list) == NULL) { - return FALSE; - } - } else if(row >= list->count(list)) { - return FALSE; - } - - // the UiList has an integrated iterator - // we only get a value to adjust it - void *val = NULL; - if(row == 0) { - val = list->first(list); - } else { - val = list->get(list, row); - } - - iter->stamp = model->stamp; - iter->user_data = list->iter; - iter->user_data2 = (gpointer)(intptr_t)row; // list->index - iter->user_data3 = val; - - return val ? TRUE : FALSE; -} - -GtkTreePath* ui_list_model_get_path( - GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), NULL); - g_return_val_if_fail(iter != NULL, NULL); - g_return_val_if_fail(iter->user_data != NULL, NULL); - - UiListModel *model = UI_LIST_MODEL(tree_model); - - GtkTreePath *path = gtk_tree_path_new(); - gtk_tree_path_append_index(path, (int)(intptr_t)iter->user_data2); // list->index - - return path; -} - -void ui_list_model_get_value( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value) -{ - g_return_if_fail(IS_UI_LIST_MODEL(tree_model)); - g_return_if_fail(iter != NULL); - g_return_if_fail(iter->user_data != NULL); - - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - - g_return_if_fail(column < model->numcolumns); - - // TODO: return correct value from column - - //value->g_type = G_TYPE_STRING; - list->iter = iter->user_data; - //list->index = (int)(intptr_t)iter->user_data2; - //list->current = iter->user_data3; - if(model->model->getvalue) { - void *data = model->model->getvalue(iter->user_data3, column); - if(model->columntypes[column] == G_TYPE_OBJECT) { - UiImage *img = data; - ui_model_set_value(model->columntypes[column], img->pixbuf, value); - } else { - ui_model_set_value(model->columntypes[column], data, value); - } - } else { - value->g_type = G_TYPE_INVALID; - } -} - -gboolean ui_list_model_iter_next( - GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); - g_return_val_if_fail(iter != NULL, FALSE); - //g_return_val_if_fail(iter->user_data != NULL, FALSE); - - if(!iter->user_data) { - return FALSE; - } - - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - list->iter = iter->user_data; - //list->index = (int)(intptr_t)iter->user_data2; - void *val = list->next(list); - iter->user_data = list->iter; - intptr_t index = (intptr_t)iter->user_data2; - index++; - //iter->user_data2 = (gpointer)(intptr_t)list->index; - iter->user_data2 = (gpointer)index; - iter->user_data3 = val; - return val ? TRUE : FALSE; -} - -gboolean ui_list_model_iter_children( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); - - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - - if(parent) { - return FALSE; - } - - /* - * a list element has no children - * we set the iter to the first element - */ - void *val = list->first(list); - iter->stamp = model->stamp; - iter->user_data = list->iter; - iter->user_data2 = (gpointer)0; - iter->user_data3 = val; - - return val ? TRUE : FALSE; -} - -gboolean ui_list_model_iter_has_child( - GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - return FALSE; -} - -gint ui_list_model_iter_n_children( - GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_assert(IS_UI_LIST_MODEL(tree_model)); - - if(!iter) { - // return number of rows - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - return list->count(list); - } - - return 0; -} - -gboolean ui_list_model_iter_nth_child( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n) -{ - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); - - if(parent) { - return FALSE; - } - - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - - // check n - if(n == 0) { - // we don't need to count if the first element is requested - if(list->first(list) == NULL) { - return FALSE; - } - } else if(n >= list->count(list)) { - return FALSE; - } - - void *val = list->get(list, n); - iter->stamp = model->stamp; - iter->user_data = list->iter; - iter->user_data2 = (gpointer)(intptr_t)n; // list->index - iter->user_data3 = val; - - return val ? TRUE : FALSE; -} - -gboolean ui_list_model_iter_parent( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - return FALSE; -} - -// ****** dnd ****** - -gboolean ui_list_model_drag_data_received( - GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - GtkSelectionData *selection_data) -{ - //printf("drag received\n"); - UiListModel *model = UI_LIST_MODEL(drag_dest); - if(model->model->drop) { - gint *indices = gtk_tree_path_get_indices(dest_path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - UiSelection s; - s.data = selection_data; - model->model->drop(&e, &s, model->var->value, row); - } - return TRUE; -} - -gboolean ui_list_model_row_drop_possible( - GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - GtkSelectionData *selection_data) -{ - //printf("row_drop_possible\n"); - UiListModel *model = UI_LIST_MODEL(drag_dest); - if(model->model->candrop) { - gint *indices = gtk_tree_path_get_indices(dest_path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - UiSelection s; - s.data = selection_data; - return model->model->candrop(&e, &s, model->var->value, row); - } - return TRUE; -} - -gboolean ui_list_model_row_draggable( - GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - //printf("row_draggable\n"); - UiListModel *model = UI_LIST_MODEL(drag_source); - if(model->model->candrag) { - gint *indices = gtk_tree_path_get_indices(path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - return model->model->candrag(&e, model->var->value, row); - } - return TRUE; -} - -gboolean ui_list_model_drag_data_get( - GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data) -{ - //printf("drag_data_get\n"); - UiListModel *model = UI_LIST_MODEL(drag_source); - if(model->model->data_get) { - gint *indices = gtk_tree_path_get_indices(path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - UiSelection s; - s.data = selection_data; - model->model->data_get(&e, &s, model->var->value, row); - } - return TRUE; -} - -gboolean ui_list_model_drag_data_delete( - GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - //printf("drag_data_delete\n"); - UiListModel *model = UI_LIST_MODEL(drag_source); - if(model->model->data_get) { - gint *indices = gtk_tree_path_get_indices(path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - model->model->data_delete(&e, model->var->value, row); - } - return TRUE; -} diff --git a/ui/gtk/model.h b/ui/gtk/model.h deleted file mode 100644 index e237826..0000000 --- a/ui/gtk/model.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 MODEL_H -#define MODEL_H - -#include "../ui/toolkit.h" -#include "../common/context.h" -#include "../ui/tree.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct UiListModel UiListModel; - -/* - * UiList to GtkTreeModel wrapper - */ -struct UiListModel { - GObject object; - UiObject *obj; - UiModel *model; - UiVar *var; - GType *columntypes; - int numcolumns; - int stamp; -}; - -/* - * initialize the class and register the type - */ -void ui_list_init(); - -/* - * Creates a UiListModel for a given UiList - */ -UiListModel* ui_list_model_new(UiObject *obj, UiVar *var, UiModel *info); - -void ui_list_model_dispose(GObject *obj); -void ui_list_model_finalize(GObject *obj); - - -// interface functions - -GtkTreeModelFlags ui_list_model_get_flags(GtkTreeModel *tree_model); - -gint ui_list_model_get_n_columns(GtkTreeModel *tree_model); - -GType ui_list_model_get_column_type(GtkTreeModel *tree_model, gint index); - -gboolean ui_list_model_get_iter( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); - -GtkTreePath* ui_list_model_get_path( - GtkTreeModel *tree_model, - GtkTreeIter *iter); - -void ui_list_model_get_value( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value); - -gboolean ui_list_model_iter_next( - GtkTreeModel *tree_model, - GtkTreeIter *iter); - -gboolean ui_list_model_iter_children( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); - -gboolean ui_list_model_iter_has_child( - GtkTreeModel *tree_model, - GtkTreeIter *iter); - -gint ui_list_model_iter_n_children( - GtkTreeModel *tree_model, - GtkTreeIter *iter); - -gboolean ui_list_model_iter_nth_child( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n); - -gboolean ui_list_model_iter_parent( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); - -/* dnd */ - -gboolean ui_list_model_drag_data_received( - GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - GtkSelectionData *selection_data); - -gboolean ui_list_model_row_drop_possible( - GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - GtkSelectionData *selection_data); - -gboolean ui_list_model_row_draggable( - GtkTreeDragSource *drag_source, - GtkTreePath *path); - -gboolean ui_list_model_drag_data_get( - GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data); - -gboolean ui_list_model_drag_data_delete( - GtkTreeDragSource *drag_source, - GtkTreePath *path); - -#ifdef __cplusplus -} -#endif - -#endif /* MODEL_H */ diff --git a/ui/gtk/objs.mk b/ui/gtk/objs.mk index ed89f4d..abad5a0 100644 --- a/ui/gtk/objs.mk +++ b/ui/gtk/objs.mk @@ -47,6 +47,7 @@ GTKOBJ += entry.o GTKOBJ += dnd.o GTKOBJ += headerbar.o GTKOBJ += webview.o +GTKOBJ += widget.o TOOLKITOBJS += $(GTKOBJ:%=$(GTK_OBJPRE)%) TOOLKITSOURCE += $(GTKOBJ:%.o=gtk/%.c) diff --git a/ui/gtk/text.c b/ui/gtk/text.c index 39f4a6b..475543c 100644 --- a/ui/gtk/text.c +++ b/ui/gtk/text.c @@ -62,6 +62,51 @@ static void selection_handler( } } +static void textarea_set_text_funcs(UiText *value) { + +} + +#if GTK_MAJOR_VERSION == 2 +static void textarea_set_undomgr(GtkWidget *text_area, UiText *value) { + GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_area)); + + if(!value->data2) { + value->data2 = ui_create_undomgr(); + } + + // register undo manager + g_signal_connect( + buf, + "insert-text", + G_CALLBACK(ui_textbuf_insert), + var); + g_signal_connect( + buf, + "delete-range", + G_CALLBACK(ui_textbuf_delete), + var); + g_signal_connect( + buf, + "mark-set", + G_CALLBACK(selection_handler), + uitext); +} +#endif + +static GtkTextBuffer* create_textbuffer(UiTextArea *textarea) { + GtkTextBuffer *buf = gtk_text_buffer_new(NULL); + if(textarea) { + g_signal_connect( + buf, + "changed", + G_CALLBACK(ui_textbuf_changed), + textarea); + } else { + fprintf(stderr, "Error: create_textbuffer: textarea == NULL\n"); + } + return buf; +} + 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); @@ -85,6 +130,8 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) { uitext->onchange = args.onchange; uitext->onchangedata = args.onchangedata; + g_object_set_data(G_OBJECT(text_area), "ui_textarea", uitext); + g_signal_connect( text_area, "destroy", @@ -114,13 +161,21 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) { // bind value if(var) { UiText *value = var->value; - GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_area)); - - if(value->value.ptr) { - gtk_text_buffer_set_text(buf, value->value.ptr, -1); - value->value.free(value->value.ptr); + GtkTextBuffer *buf; + if(value->data1 && value->datatype == UI_TEXT_TYPE_BUFFER) { + buf = value->data1; + } else { + buf = create_textbuffer(uitext); + if(value->value.ptr) { + gtk_text_buffer_set_text(buf, value->value.ptr, -1); + value->value.free(value->value.ptr); + } } - + gtk_text_view_set_buffer(GTK_TEXT_VIEW(text_area), buf); + value->obj = text_area; + value->save = ui_textarea_save; + value->restore = ui_textarea_restore; + value->destroy = ui_textarea_text_destroy; value->get = ui_textarea_get; value->set = ui_textarea_set; value->getsubstr = ui_textarea_getsubstr; @@ -130,35 +185,15 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) { value->selection = ui_textarea_selection; value->length = ui_textarea_length; value->remove = ui_textarea_remove; + value->data1 = buf; + value->data2 = NULL; + value->datatype == UI_TEXT_TYPE_BUFFER; value->value.ptr = NULL; value->value.free = NULL; - value->obj = buf; - if(!value->undomgr) { - value->undomgr = ui_create_undomgr(); - } - - g_signal_connect( - buf, - "changed", - G_CALLBACK(ui_textbuf_changed), - uitext); - // register undo manager - g_signal_connect( - buf, - "insert-text", - G_CALLBACK(ui_textbuf_insert), - var); - g_signal_connect( - buf, - "delete-range", - G_CALLBACK(ui_textbuf_delete), - var); - g_signal_connect( - buf, - "mark-set", - G_CALLBACK(selection_handler), - uitext); +#if GTK_MAJOR_VERSION == 2 + textarea_set_undomgr(text_area, value); +#endif } return scroll_area; @@ -175,11 +210,29 @@ UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea) { return SCROLLEDWINDOW_GET_CHILD(textarea); } +void ui_textarea_save(UiText *text) { + // NOOP +} + +void ui_textarea_restore(UiText *text) { + GtkWidget *textarea = text->obj; + if(!text->data1) { + text->data1 = create_textbuffer(g_object_get_data(G_OBJECT(textarea), "ui_textarea")); + text->datatype = UI_TEXT_TYPE_BUFFER; + } + gtk_text_view_set_buffer(GTK_TEXT_VIEW(textarea), text->data1); +} + +void ui_textarea_text_destroy(UiText *text) { + GtkTextBuffer *buf = text->data1; + g_object_unref(buf); +} + char* ui_textarea_get(UiText *text) { if(text->value.ptr) { text->value.free(text->value.ptr); } - GtkTextBuffer *buf = text->obj; + GtkTextBuffer *buf = text->data1; GtkTextIter start; GtkTextIter end; gtk_text_buffer_get_bounds(buf, &start, &end); @@ -190,7 +243,7 @@ char* ui_textarea_get(UiText *text) { } void ui_textarea_set(UiText *text, const char *str) { - gtk_text_buffer_set_text((GtkTextBuffer*)text->obj, str, -1); + gtk_text_buffer_set_text((GtkTextBuffer*)text->data1, str, -1); if(text->value.ptr) { text->value.free(text->value.ptr); } @@ -202,11 +255,11 @@ char* ui_textarea_getsubstr(UiText *text, int begin, int end) { if(text->value.ptr) { text->value.free(text->value.ptr); } - GtkTextBuffer *buf = text->obj; + GtkTextBuffer *buf = text->data1; GtkTextIter ib; GtkTextIter ie; - gtk_text_buffer_get_iter_at_offset(text->obj, &ib, begin); - gtk_text_buffer_get_iter_at_offset(text->obj, &ie, end); + gtk_text_buffer_get_iter_at_offset(text->data1, &ib, begin); + gtk_text_buffer_get_iter_at_offset(text->data1, &ie, end); char *str = gtk_text_buffer_get_text(buf, &ib, &ie, FALSE); text->value.ptr = g_strdup(str); text->value.free = (ui_freefunc)g_free; @@ -215,8 +268,8 @@ char* ui_textarea_getsubstr(UiText *text, int begin, int end) { void ui_textarea_insert(UiText *text, int pos, char *str) { GtkTextIter offset; - gtk_text_buffer_get_iter_at_offset(text->obj, &offset, pos); - gtk_text_buffer_insert(text->obj, &offset, str, -1); + gtk_text_buffer_get_iter_at_offset(text->data1, &offset, pos); + gtk_text_buffer_insert(text->data1, &offset, str, -1); if(text->value.ptr) { text->value.free(text->value.ptr); } @@ -226,14 +279,14 @@ void ui_textarea_insert(UiText *text, int pos, char *str) { void ui_textarea_setposition(UiText *text, int pos) { GtkTextIter iter; - gtk_text_buffer_get_iter_at_offset(text->obj, &iter, pos); - gtk_text_buffer_place_cursor(text->obj, &iter); + gtk_text_buffer_get_iter_at_offset(text->data1, &iter, pos); + gtk_text_buffer_place_cursor(text->data1, &iter); } int ui_textarea_position(UiText *text) { GtkTextIter begin; GtkTextIter end; - gtk_text_buffer_get_selection_bounds(text->obj, &begin, &end); + gtk_text_buffer_get_selection_bounds(text->data1, &begin, &end); text->pos = gtk_text_iter_get_offset(&begin); return text->pos; } @@ -241,13 +294,13 @@ int ui_textarea_position(UiText *text) { void ui_textarea_selection(UiText *text, int *begin, int *end) { GtkTextIter b; GtkTextIter e; - gtk_text_buffer_get_selection_bounds(text->obj, &b, &e); + gtk_text_buffer_get_selection_bounds(text->data1, &b, &e); *begin = gtk_text_iter_get_offset(&b); *end = gtk_text_iter_get_offset(&e); } int ui_textarea_length(UiText *text) { - GtkTextBuffer *buf = text->obj; + GtkTextBuffer *buf = text->data1; GtkTextIter start; GtkTextIter end; gtk_text_buffer_get_bounds(buf, &start, &end); @@ -255,7 +308,7 @@ int ui_textarea_length(UiText *text) { } void ui_textarea_remove(UiText *text, int begin, int end) { - GtkTextBuffer *buf = text->obj; + GtkTextBuffer *buf = text->data1; GtkTextIter ib; GtkTextIter ie; gtk_text_buffer_get_iter_at_offset(buf, &ib, begin); @@ -278,6 +331,7 @@ void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea) { e.document = textarea->ctx->document; e.eventdata = value; e.intval = 0; + e.set = ui_get_setop(); if(textarea->onchange) { textarea->onchange(&e, textarea->onchangedata); @@ -299,10 +353,10 @@ void ui_textbuf_insert( { UiVar *var = data; UiText *value = var->value; - if(!value->undomgr) { - value->undomgr = ui_create_undomgr(); + if(!value->data2) { + value->data2 = ui_create_undomgr(); } - UiUndoMgr *mgr = value->undomgr; + UiUndoMgr *mgr = value->data2; if(!mgr->event) { return; } @@ -371,10 +425,10 @@ void ui_textbuf_delete( { UiVar *var = data; UiText *value = var->value; - if(!value->undomgr) { - value->undomgr = ui_create_undomgr(); + if(!value->data2) { + value->data2 = ui_create_undomgr(); } - UiUndoMgr *mgr = value->undomgr; + UiUndoMgr *mgr = value->data2; if(!mgr->event) { return; } @@ -469,7 +523,7 @@ int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) { } void ui_text_undo(UiText *value) { - UiUndoMgr *mgr = value->undomgr; + UiUndoMgr *mgr = value->data2; if(mgr->cur) { UiTextBufOp *op = mgr->cur; @@ -498,7 +552,7 @@ void ui_text_undo(UiText *value) { } void ui_text_redo(UiText *value) { - UiUndoMgr *mgr = value->undomgr; + UiUndoMgr *mgr = value->data2; UiTextBufOp *elm = NULL; if(mgr->cur) { @@ -638,6 +692,7 @@ void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) { e.document = textfield->obj->ctx->document; e.eventdata = value; e.intval = 0; + e.set = ui_get_setop(); if(textfield->onchange) { textfield->onchange(&e, textfield->onchangedata); @@ -656,6 +711,7 @@ void ui_textfield_activate(GtkEntry* self, UiTextField *textfield) { e.document = textfield->obj->ctx->document; e.eventdata = NULL; e.intval = 0; + e.set = ui_get_setop(); textfield->onactivate(&e, textfield->onactivatedata); } } @@ -752,6 +808,7 @@ void ui_path_button_clicked(GtkWidget *widget, UiEventDataExt *event) { evt.document = evt.obj->ctx->document; evt.eventdata = elm->path; evt.intval = event->value0; + evt.set = ui_get_setop(); event->callback(&evt, event->userdata); free(path.ptr); } diff --git a/ui/gtk/text.h b/ui/gtk/text.h index f7b8ad7..b7fbda4 100644 --- a/ui/gtk/text.h +++ b/ui/gtk/text.h @@ -111,6 +111,9 @@ typedef struct UiPathTextField { UIWIDGET ui_textarea_var(UiObject *obj, UiVar *var); void ui_textarea_destroy(GtkWidget *object, UiTextArea *textarea); +void ui_textarea_save(UiText *text); +void ui_textarea_restore(UiText *text); +void ui_textarea_text_destroy(UiText *text); char* ui_textarea_get(UiText *text); void ui_textarea_set(UiText *text, const char *str); char* ui_textarea_getsubstr(UiText *text, int begin, int end); diff --git a/ui/gtk/toolkit.c b/ui/gtk/toolkit.c index 19a0990..7e8e3a5 100644 --- a/ui/gtk/toolkit.c +++ b/ui/gtk/toolkit.c @@ -248,8 +248,9 @@ void ui_set_show_all(UIWIDGET widget, int value) { } void ui_set_visible(UIWIDGET widget, int visible) { - // TODO: gtk4 -#if GTK_MAJOR_VERSION <= 3 +#if GTK_MAJOR_VERSION >= 4 + gtk_widget_set_visible(widget, visible); +#else if(visible) { gtk_widget_set_no_show_all(widget, FALSE); gtk_widget_show_all(widget); @@ -327,8 +328,6 @@ UiObject *ui_get_active_window() { #if GTK_MAJOR_VERSION >= 3 -static GtkCssProvider* ui_gtk_css_provider; - #if GTK_MAJOR_VERSION == 4 static const char *ui_gtk_css = "#path-textfield-box {\n" @@ -415,15 +414,19 @@ static const char *ui_gtk_css = #endif void ui_css_init(void) { - ui_gtk_css_provider = gtk_css_provider_new(); + ui_add_styledata(ui_gtk_css, -1); +} + +void ui_add_styledata(const char *styledata, int len) { + GtkCssProvider *css = gtk_css_provider_new(); #ifdef UI_GTK3 - gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -1, NULL); + gtk_css_provider_load_from_data(css, styledata, len, NULL); GdkScreen *screen = gdk_screen_get_default(); gtk_style_context_add_provider_for_screen( screen, - GTK_STYLE_PROVIDER(ui_gtk_css_provider), + GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_USER); #endif /* UI_GTK3 */ @@ -431,13 +434,20 @@ void ui_css_init(void) { #if GTK_MINOR_VERSION < 12 - gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -1); + gtk_css_provider_load_from_data(css, styledata, len); #else - gtk_css_provider_load_from_string(ui_gtk_css_provider, ui_gtk_css); + if(len < 0) { + gtk_css_provider_load_from_string(css, ui_gtk_css); + } else { + GBytes *style_data = g_bytes_new(styledata, len); + gtk_css_provider_load_from_bytes(css, style_data); + g_bytes_unref(style_data); + + } #endif /* GTK_MINOR_VERSION < 12 */ GdkDisplay *display = gdk_display_get_default(); - gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(ui_gtk_css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); #endif /* UI_GTK4 */ } diff --git a/ui/gtk/toolkit.h b/ui/gtk/toolkit.h index 62e82bc..b98bb25 100644 --- a/ui/gtk/toolkit.h +++ b/ui/gtk/toolkit.h @@ -74,6 +74,8 @@ 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 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 #define WINDOW_SHOW(window) gtk_widget_show_all(window) #define WINDOW_DESTROY(window) gtk_widget_destroy(window) @@ -94,6 +96,8 @@ 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 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 #ifdef UI_GTK2 diff --git a/ui/gtk/tree.c b/ui/gtk/tree.c deleted file mode 100644 index d93a6f0..0000000 --- a/ui/gtk/tree.c +++ /dev/null @@ -1,562 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 -#include -#include -#include - -#include "../common/context.h" -#include "../common/object.h" -#include "container.h" - -#include "tree.h" - - -void* ui_strmodel_getvalue(void *elm, int column) { - return column == 0 ? elm : NULL; -} - - -UIWIDGET ui_listview_str(UiObject *obj, UiList *list, ui_callback f, void *udata) { - return ui_listview(obj, list, ui_strmodel_getvalue, f, udata); -} - -UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - // create treeview - GtkWidget *view = gtk_tree_view_new(); - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); - GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "text", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); - - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); -#ifdef UI_GTK3 -#if GTK_MINOR_VERSION >= 8 - gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE); -#else - // TODO: implement for older gtk3 -#endif -#else - // TODO: implement for gtk2 -#endif - - UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - model->getvalue = getvalue; - UiList *list = var->value; - UiListModel *listmodel = ui_list_model_new(obj, var, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - - UiListView *listview = malloc(sizeof(UiListView)); - listview->obj = obj; - listview->widget = view; - listview->var = var; - listview->model = model; - g_signal_connect( - view, - "destroy", - G_CALLBACK(ui_listview_destroy), - listview); - - // bind var - list->update = ui_listview_update; - list->obj = listview; - - // add callback - if(f) { - UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData)); - event->obj = obj; - event->userdata = udata; - event->activate = f; - event->selection = NULL; - - g_signal_connect( - view, - "row-activated", - G_CALLBACK(ui_listview_activate_event), - event); - } - - // add widget to the current container - GtkWidget *scroll_area = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy( - GTK_SCROLLED_WINDOW(scroll_area), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS - gtk_container_add(GTK_CONTAINER(scroll_area), view); - - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, scroll_area, TRUE); - - // ct->current should point to view, not scroll_area, to make it possible - // to add a context menu - ct->current = view; - - return scroll_area; -} - -UIWIDGET ui_listview(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_listview_var(obj, var, getvalue, f, udata); -} - -UIWIDGET ui_listview_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - return ui_listview_var(obj, var, getvalue, f, udata); - } else { - // TODO: error - } - return NULL; -} - -static void drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer udata) { - printf("drag begin\n"); - -} - -static void drag_end( - GtkWidget *widget, - GdkDragContext *context, - guint time, - gpointer udata) -{ - printf("drag end\n"); - -} - -static GtkTargetEntry targetentries[] = - { - { "STRING", 0, 0 }, - { "text/plain", 0, 1 }, - { "text/uri-list", 0, 2 }, - }; - -UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb) { - // create treeview - GtkWidget *view = gtk_tree_view_new(); - - int addi = 0; - for(int i=0;icolumns;i++) { - GtkTreeViewColumn *column = NULL; - if(model->types[i] == UI_ICON_TEXT) { - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(column, model->titles[i]); - - GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new(); - GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new(); - - gtk_tree_view_column_pack_end(column, textrenderer, TRUE); - 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); - - addi++; - } else { - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes( - model->titles[i], - renderer, - "text", - i + addi, - NULL); - } - gtk_tree_view_column_set_resizable(column, TRUE); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); - } - - //gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); -#ifdef UI_GTK3 - //gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE); -#else - -#endif - - UiList *list = var->value; - UiListModel *listmodel = ui_list_model_new(obj, var, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - - //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)); - tableview->obj = obj; - tableview->widget = view; - tableview->var = var; - tableview->model = model; - g_signal_connect( - view, - "destroy", - G_CALLBACK(ui_listview_destroy), - tableview); - - // bind var - list->update = ui_listview_update; - list->obj = tableview; - - // add callback - UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData)); - event->obj = obj; - event->activate = cb.activate; - event->selection = cb.selection; - event->userdata = cb.userdata; - if(cb.activate) { - g_signal_connect( - view, - "row-activated", - G_CALLBACK(ui_listview_activate_event), - event); - } - if(cb.selection) { - GtkTreeSelection *selection = gtk_tree_view_get_selection( - GTK_TREE_VIEW(view)); - g_signal_connect( - selection, - "changed", - G_CALLBACK(ui_listview_selection_event), - event); - } - // TODO: destroy callback - - - GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view)); - gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); - - // add widget to the current container - GtkWidget *scroll_area = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy( - GTK_SCROLLED_WINDOW(scroll_area), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS - gtk_container_add(GTK_CONTAINER(scroll_area), view); - - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, scroll_area, TRUE); - - // ct->current should point to view, not scroll_area, to make it possible - // to add a context menu - ct->current = view; - - return scroll_area; -} - -UIWIDGET ui_table(UiObject *obj, UiList *list, UiModel *model, UiListCallbacks cb) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_table_var(obj, var, model, cb); -} - -UIWIDGET ui_table_nv(UiObject *obj, char *varname, UiModel *model, UiListCallbacks cb) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - return ui_table_var(obj, var, model, cb); - } else { - // TODO: error - } - return NULL; -} - -GtkWidget* ui_get_tree_widget(UIWIDGET widget) { - GList *c = gtk_container_get_children(GTK_CONTAINER(widget)); - if(c) { - return c->data; - } - return NULL; -} - -static char** targets2array(char *target0, va_list ap, int *nelm) { - int al = 16; - char **targets = calloc(16, sizeof(char*)); - targets[0] = target0; - - int i = 1; - char *target; - while((target = va_arg(ap, char*)) != NULL) { - if(i >= al) { - al *= 2; - targets = realloc(targets, al*sizeof(char*)); - } - targets[i] = target; - i++; - } - - *nelm = i; - return targets; -} - -static GtkTargetEntry* targetstr2gtktargets(char **str, int nelm) { - GtkTargetEntry *targets = calloc(nelm, sizeof(GtkTargetEntry)); - for(int i=0;iobj; - UiListModel *model = ui_list_model_new(view->obj, view->var, view->model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(model)); - g_object_unref(G_OBJECT(model)); - // TODO: free old model -} - -void ui_listview_destroy(GtkWidget *w, UiListView *v) { - gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL); - ui_destroy_boundvar(v->obj->ctx, v->var); - // TODO: destroy model? - free(v); -} - -void ui_combobox_destroy(GtkWidget *w, UiListView *v) { - gtk_combo_box_set_model(GTK_COMBO_BOX(w), NULL); - ui_destroy_boundvar(v->obj->ctx, v->var); - // TODO: destroy model? - free(v); -} - - -void ui_listview_activate_event( - GtkTreeView *treeview, - GtkTreePath *path, - GtkTreeViewColumn *column, - UiTreeEventData *event) -{ - UiListSelection *selection = ui_listview_selection( - gtk_tree_view_get_selection(treeview), - event); - - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.eventdata = selection; - e.intval = selection->count > 0 ? selection->rows[0] : -1; - event->activate(&e, event->userdata); - - if(selection->count > 0) { - free(selection->rows); - } - free(selection); -} - -void ui_listview_selection_event( - GtkTreeSelection *treeselection, - UiTreeEventData *event) -{ - UiListSelection *selection = ui_listview_selection(treeselection, event); - - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.eventdata = selection; - e.intval = selection->count > 0 ? selection->rows[0] : -1; - event->selection(&e, event->userdata); - - if(selection->count > 0) { - free(selection->rows); - } - free(selection); -} - -UiListSelection* ui_listview_selection( - GtkTreeSelection *selection, - UiTreeEventData *event) -{ - GList *rows = gtk_tree_selection_get_selected_rows(selection, NULL); - - UiListSelection *ls = malloc(sizeof(UiListSelection)); - ls->count = g_list_length(rows); - ls->rows = calloc(ls->count, sizeof(int)); - GList *r = rows; - int i = 0; - while(r) { - GtkTreePath *path = r->data; - ls->rows[i] = ui_tree_path_list_index(path); - r = r->next; - i++; - } - return ls; -} - -int ui_tree_path_list_index(GtkTreePath *path) { - int depth = gtk_tree_path_get_depth(path); - if(depth == 0) { - fprintf(stderr, "UiError: treeview selection: depth == 0\n"); - return -1; - } - int *indices = gtk_tree_path_get_indices(path); - return indices[depth - 1]; -} - - -/* --------------------------- ComboBox --------------------------- */ - -UIWIDGET ui_combobox_str(UiObject *obj, UiList *list, ui_callback f, void *udata) { - return ui_combobox(obj, list, ui_strmodel_getvalue, f, udata); -} - -UIWIDGET ui_combobox(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_combobox_var(obj, var, getvalue, f, udata); -} - -UIWIDGET ui_combobox_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - return ui_combobox_var(obj, var, getvalue, f, udata); - } else { - // TODO: error - } - return NULL; -} - -UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - model->getvalue = getvalue; - UiListModel *listmodel = ui_list_model_new(obj, var, model); - - GtkWidget *combobox = ui_create_combobox(obj, listmodel, f, udata); - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, combobox, FALSE); - return combobox; -} - -GtkWidget* ui_create_combobox(UiObject *obj, UiListModel *model, ui_callback f, void *udata) { - GtkWidget *combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model)); - - UiListView *uicbox = malloc(sizeof(UiListView)); - uicbox->obj = obj; - uicbox->widget = combobox; - uicbox->var = model->var; - uicbox->model = model->model; - - g_signal_connect( - combobox, - "destroy", - G_CALLBACK(ui_combobox_destroy), - uicbox); - - // bind var - UiList *list = model->var->value; - list->update = ui_combobox_modelupdate; - 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( - GTK_CELL_LAYOUT(combobox), - renderer, - "text", - 0, - NULL); - gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0); - - // add callback - if(f) { - UiEventData *event = ui_malloc(obj->ctx, sizeof(UiEventData)); - event->obj = obj; - event->userdata = udata; - event->callback = f; - event->value = 0; - - g_signal_connect( - combobox, - "changed", - G_CALLBACK(ui_combobox_change_event), - event); - } - - return combobox; -} - -void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) { - UiEvent event; - event.obj = e->obj; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - event.eventdata = NULL; - event.intval = gtk_combo_box_get_active(widget); - e->callback(&event, e->userdata); -} - -void ui_combobox_modelupdate(UiList *list, int i) { - UiListView *view = list->obj; - UiListModel *model = ui_list_model_new(view->obj, view->var, view->model); - gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(model)); -} - diff --git a/ui/gtk/tree.h b/ui/gtk/tree.h deleted file mode 100644 index e771ea1..0000000 --- a/ui/gtk/tree.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TREE_H -#define TREE_H - -#include "../ui/tree.h" -#include "toolkit.h" -#include "model.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct UiListView { - UiObject *obj; - GtkWidget *widget; - UiVar *var; - UiModel *model; -} UiListView; - -typedef struct UiTreeEventData { - UiObject *obj; - ui_callback activate; - ui_callback selection; - void *userdata; -} UiTreeEventData; - -void* ui_strmodel_getvalue(void *elm, int column); - -UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); -UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb); - -GtkWidget* ui_get_tree_widget(UIWIDGET widget); - -void ui_listview_update(UiList *list, int i); -void ui_combobox_destroy(GtkWidget *w, UiListView *v); -void ui_listview_destroy(GtkWidget *w, UiListView *v); - -void ui_listview_activate_event( - GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - UiTreeEventData *event); -void ui_listview_selection_event( - GtkTreeSelection *treeselection, - UiTreeEventData *event); -UiListSelection* ui_listview_selection( - GtkTreeSelection *selection, - UiTreeEventData *event); -int ui_tree_path_list_index(GtkTreePath *path); - -UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); -GtkWidget* ui_create_combobox(UiObject *obj, UiListModel *model, ui_callback f, void *udata); -void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e); -void ui_combobox_modelupdate(UiList *list, int i); - -#ifdef __cplusplus -} -#endif - -#endif /* TREE_H */ - diff --git a/ui/gtk/webview.h b/ui/gtk/webview.h index c06cfe2..e350858 100644 --- a/ui/gtk/webview.h +++ b/ui/gtk/webview.h @@ -31,7 +31,12 @@ #ifdef UI_WEBVIEW #include "../ui/webview.h" + +#if GTK_MAJOR_VERSION >= 4 #include +#else +#include +#endif #ifdef __cplusplus extern "C" { diff --git a/ui/gtk/widget.c b/ui/gtk/widget.c new file mode 100644 index 0000000..505bca6 --- /dev/null +++ b/ui/gtk/widget.c @@ -0,0 +1,60 @@ +/* + * 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 "widget.h" +#include "container.h" + +#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_LAYOUT1(current, args); + current->container->add(current->container, widget, FALSE); + + 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, FALSE); + return widget; +} + +void ui_widget_set_size(UIWIDGET w, int width, int height) { + gtk_widget_set_size_request(w, width, height); +} + +void ui_widget_redraw(UIWIDGET w) { + gtk_widget_queue_draw(w); +} diff --git a/ucx/ucx.c b/ui/gtk/widget.h similarity index 59% rename from ucx/ucx.c rename to ui/gtk/widget.h index 923330d..57c7314 100644 --- a/ucx/ucx.c +++ b/ui/gtk/widget.h @@ -1,24 +1,7 @@ -/** - * @mainpage UAP Common Extensions - * Library with common and useful functions, macros and data structures. - *

- * Latest available source:
- * - * https://sourceforge.net/projects/ucx/files/ - *

- * - *

- * Repositories:
- * - * https://sourceforge.net/p/ucx/code - * - or - - * - * https://develop.uap-core.de/hg/ucx - *

- * - *

LICENCE

- * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. +/* + * 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: @@ -43,20 +26,21 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/ucx.h" - -int ucx_szmul_impl(size_t a, size_t b, size_t *result) { - if(a == 0 || b == 0) { - *result = 0; - return 0; - } - size_t r = a * b; - if(r / b == a) { - *result = r; - return 0; - } else { - *result = 0; - return 1; - } +#ifndef WIDGET_H +#define WIDGET_H + +#include "../ui/widget.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus } +#endif + +#endif /* WIDGET_H */ diff --git a/ui/ui/container.h b/ui/ui/container.h index c7d6523..3f828ea 100644 --- a/ui/ui/container.h +++ b/ui/ui/container.h @@ -57,18 +57,6 @@ typedef enum UiHeaderbarAlternative { UI_HEADERBAR_ALTERNATIVE_BOX } UiHeaderbarAlternative; -typedef struct UiWidgetArgs { - UiTri fill; - UiBool hexpand; - UiBool vexpand; - UiBool hfill; - UiBool vfill; - UiBool override_defaults; - int colspan; - int rowspan; - const char *name; - const char *style_class; -} UiWidgetArgs; typedef struct UiContainerArgs { UiTri fill; @@ -267,13 +255,20 @@ struct UiContainerX { #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_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)) +#define ui_grid_w(obj, w, ...) for(w = ui_grid_create(obj, (UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_tabview_w(obj, w, ...) for(w = ui_tabview_create(obj, (UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_scrolledwindow_w(obj, w, ...) for(w = ui_scrolledwindow_create(obj, (UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_hsplitpane(obj, ...) for(ui_hsplitpane_create(obj, (UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_vsplitpane(obj, ...) for(ui_vsplitpane_create(obj, (UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_hsplitpane0(obj) for(ui_hsplitpane_create(obj, (UiSplitPaneArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_vsplitpane0(obj) for(ui_vsplitpane_create(obj, (UiSplitPaneArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_hsplitpane_w(obj, w, ...) for(w = ui_hsplitpane_create(obj, (UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_vsplitpane_w(obj, w, ...) for(w = ui_vsplitpane_create(obj, (UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) + #define ui_tab(obj, label) for(ui_tab_create(obj, label);ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_headerbar_start(obj) for(ui_headerbar_start_create(obj);ui_container_finish(obj);ui_container_begin_close(obj)) @@ -310,6 +305,7 @@ UIEXPORT UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs args UIEXPORT UIWIDGET ui_hsplitpane_create(UiObject *obj, UiSplitPaneArgs args); 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); @@ -330,17 +326,6 @@ UIEXPORT UiTabbedPane* ui_tabbed_document_view(UiObject *obj); UIEXPORT UiObject* ui_document_tab(UiTabbedPane *view); -#ifdef UI_GTK -typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs args, void *userdata); -#elif defined(UI_MOTIF) -typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs args, void *userdata, Widget parent, Arg *a, int n); -#elif defined(UI_COCOA) -typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs args, void *userdata); -#endif -UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs args); - -#define ui_customwidget(obj, create_widget, userdata, ...) ui_customwidget_create(obj, create_widget, userdata, (UiWidgetArgs) { __VA_ARGS__ }) - /* used for macro */ UIEXPORT void ui_container_begin_close(UiObject *obj); diff --git a/ui/ui/display.h b/ui/ui/display.h index 58b4c84..8ed861b 100644 --- a/ui/ui/display.h +++ b/ui/ui/display.h @@ -123,7 +123,6 @@ UIEXPORT UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs args); UIEXPORT UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs args); UIWIDGET ui_space_deprecated(UiObject *obj); -UIWIDGET ui_separator_deprecated(UiObject *obj); /* progress bar/spinner */ diff --git a/ui/ui/image.h b/ui/ui/image.h index 94ab8a6..7217caa 100644 --- a/ui/ui/image.h +++ b/ui/ui/image.h @@ -37,6 +37,13 @@ extern "C" { #define UI_IMAGE_OBJECT_TYPE "image" +#ifdef UI_GTK +#define UIIMAGE GdkPixbuf* +#else +#define UIIMAGE void* +#endif + + typedef struct UiImageViewerArgs { UiTri fill; UiBool hexpand; @@ -51,16 +58,37 @@ typedef struct UiImageViewerArgs { UiBool scrollarea; UiBool autoscale; + UiBool adjustwidgetsize; + UiBool useradjustable; + int image_padding; + int image_padding_left; + int image_padding_right; + int image_padding_top; + int image_padding_bottom; UiGeneric *value; const char *varname; UiMenuBuilder *contextmenu; + + ui_callback onbuttonpress; + void *onbuttonpressdata; + ui_callback onbuttonrelease; + void *onbuttonreleasedata; } UiImageViewerArgs; #define ui_imageviewer(obj, ...) ui_imageviewer_create(obj, (UiImageViewerArgs){ __VA_ARGS__ } ) UIEXPORT UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args); +UIEXPORT UIWIDGET ui_imageviewer_reset(UIWIDGET w); +UIEXPORT UIWIDGET ui_imageviewer_set_autoscale(UIWIDGET w, UiBool set); +UIEXPORT UIWIDGET ui_imageviewer_set_adjustwidgetsize(UIWIDGET w, UiBool set); +UIEXPORT UIWIDGET ui_imageviewer_set_useradjustable(UIWIDGET w, UiBool set); + UIEXPORT int ui_image_load_file(UiGeneric *obj, const char *path); +UIEXPORT int ui_image_load_data(UiGeneric *obj, const void *imgdata, size_t size); + +UIEXPORT void ui_image_ref(UIIMAGE img); +UIEXPORT void ui_image_unref(UIIMAGE img); #ifdef __cplusplus } diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index dd4a7d1..2af07c5 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -30,6 +30,7 @@ #define UI_TOOLKIT_H #include +#include #ifdef UI_COCOA @@ -60,24 +61,41 @@ typedef void* UIMENU; // NSMenu* #include #endif -#elif UI_MOTIF - -#include -#define UIWIDGET Widget -#define UIMENU Widget - #elif defined(UI_QT4) || defined(UI_QT5) +#define UI_QT + #ifdef __cplusplus + #include #include #include + #define UIWIDGET QWidget* +#define UIWINDOW QWidget* #define UIMENU QMenu* #else /* __cplusplus */ #define UIWIDGET void* +#define UIWINDOW void* #define UIMENU void* #endif +#elif UI_MOTIF + +#include +#define UIWIDGET Widget +#define UIMENU Widget + + +#elif UI_WIN32 + +#include + +#define UIEXPORT __declspec(dllexport) + +#define UIWIDGET void* +#define UIWINDOW void* +#define UIMENU void* + #elif UI_WINUI #define UIEXPORT __declspec(dllexport) @@ -152,7 +170,11 @@ extern "C" { #define UI_GROUPS(...) (const int[]){ __VA_ARGS__, -1 } /* public types */ +#ifndef __cplusplus typedef _Bool UiBool; +#else +typedef bool UiBool; +#endif typedef struct UiObject UiObject; typedef struct UiContainerX UiContainerX; @@ -189,16 +211,29 @@ typedef struct UiThreadpool UiThreadpool; typedef struct UiTabbedPane UiTabbedPane; -typedef enum UiTri UiTri; -typedef enum UiLabelType UiLabelType; +typedef enum UiTri { + UI_DEFAULT = 0, + UI_ON, + UI_OFF +} UiTri; -typedef enum UiDnDAction UiDnDAction; enum UiMouseEventType { UI_PRESS = 0, UI_PRESS2 }; -enum UiLabelType { UI_LABEL_DEFAULT, UI_LABEL_TEXT, UI_LABEL_ICON, UI_LABEL_TEXT_ICON }; - -enum UiDnDAction { UI_DND_ACTION_NONE, UI_DND_ACTION_COPY, UI_DND_ACTION_MOVE, UI_DND_ACTION_LINK, UI_DND_ACTION_CUSTOM }; +typedef enum UiLabelType { + UI_LABEL_DEFAULT, + UI_LABEL_TEXT, + UI_LABEL_ICON, + UI_LABEL_TEXT_ICON +} UiLabelType; + +typedef enum UiDnDAction { + UI_DND_ACTION_NONE, + UI_DND_ACTION_COPY, + UI_DND_ACTION_MOVE, + UI_DND_ACTION_LINK, + UI_DND_ACTION_CUSTOM +} UiDnDAction; typedef void(*ui_callback)(UiEvent*, void*); /* event, user data */ @@ -284,6 +319,7 @@ struct UiEvent { void *window; void *eventdata; int intval; + int set; }; struct UiMouseEvent { @@ -332,23 +368,32 @@ struct UiString { }; struct UiText { + void (*save)(UiText*); + void (*destroy)(UiText*); + void (*restore)(UiText*); void (*set)(UiText*, const char*); char* (*get)(UiText*); char* (*getsubstr)(UiText*, int, int); /* text, begin, end */ void (*insert)(UiText*, int, char*); void (*setposition)(UiText*,int); int (*position)(UiText*); + void (*setselection)(UiText*, int, int); /* text, begin, end */ void (*selection)(UiText*, int*, int*); /* text, begin, end */ int (*length)(UiText*); void (*remove)(UiText*, int, int); /* text, begin, end */ UiStr value; int pos; void *obj; - void *undomgr; + int datatype; + void *data1; + void *data2; // TODO: replacefunc, ... UiObserver *observers; }; +/* UiText.datatype */ +#define UI_TEXT_TYPE_BUFFER 1 + struct UiGeneric { void* (*get)(UiGeneric*); const char* (*get_type)(UiGeneric*); @@ -415,12 +460,6 @@ struct UiRange { UiObserver *observers; }; -enum UiTri { - UI_DEFAULT = 0, - UI_ON, - UI_OFF -}; - struct UiFileList { char **files; size_t nfiles; @@ -435,6 +474,8 @@ typedef struct UiCondVar { UIEXPORT void ui_init(const char *appname, int argc, char **argv); UIEXPORT const char* ui_appname(); +UIEXPORT void ui_add_styledata(const char *styledata, int len); + UIEXPORT UiContext* ui_global_context(void); UIEXPORT void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata); @@ -580,6 +621,9 @@ UIEXPORT void ui_condvar_wait(UiCondVar *var); UIEXPORT void ui_condvar_signal(UiCondVar *var, void *data, int intdata); UIEXPORT void ui_condvar_destroy(UiCondVar *var); +UIEXPORT void ui_setop_enable(int set); +UIEXPORT int ui_get_setop(void); + #ifdef __cplusplus } #endif diff --git a/ui/ui/tree.h b/ui/ui/tree.h index 589d2a1..8217cf6 100644 --- a/ui/ui/tree.h +++ b/ui/ui/tree.h @@ -118,6 +118,8 @@ struct UiListArgs { UiList* list; const char* varname; UiModel* model; + char **static_elements; + size_t static_nelm; ui_getvaluefunc getvalue; ui_callback onactivate; void* onactivatedata; @@ -249,6 +251,9 @@ UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs args); UIEXPORT UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs args); UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject* obj, UiListArgs args); +UIEXPORT void ui_listview_select(UIWIDGET listview, int index); +UIEXPORT void ui_combobox_select(UIWIDGET dropdown, int index); + UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args); diff --git a/ui/ui/ui.h b/ui/ui/ui.h index 41883f1..57c8025 100644 --- a/ui/ui/ui.h +++ b/ui/ui/ui.h @@ -30,6 +30,7 @@ #define UI_H #include "toolkit.h" +#include "widget.h" #include "container.h" #include "menu.h" #include "toolbar.h" diff --git a/ui/ui/widget.h b/ui/ui/widget.h new file mode 100644 index 0000000..e08233d --- /dev/null +++ b/ui/ui/widget.h @@ -0,0 +1,81 @@ +/* + * 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_WIDGET_H +#define UI_WIDGET_H + +#include "toolkit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UiWidgetArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + UiBool override_defaults; + int colspan; + int rowspan; + const char *name; + const char *style_class; +} UiWidgetArgs; + +#ifdef UI_GTK +typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs args, void *userdata); +#elif defined(UI_QT) +typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs args, void *userdata); +#elif defined(UI_MOTIF) +typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs args, void *userdata, Widget parent, Arg *a, int n); +#elif defined(UI_COCOA) +typedef UIWIDGET (*ui_createwidget_func)(UiObject *obj, UiWidgetArgs args, void *userdata); +#elif defined(UI_WINUI) +typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs args, void *userdata); +#elif defined(UI_WIN32) +typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs args, void *userdata); +#endif +UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs args); + +#define ui_customwidget(obj, create_widget, userdata, ...) ui_customwidget_create(obj, create_widget, userdata, (UiWidgetArgs) { __VA_ARGS__ }) + + +UIEXPORT UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args); + +#define ui_separator(obj, ...) ui_separator_create(obj, &(UiWidgetArgs){ __VA_ARGS__ } ) + +UIEXPORT void ui_widget_set_size(UIWIDGET w, int width, int height); +UIEXPORT void ui_widget_redraw(UIWIDGET w); + + +#ifdef __cplusplus +} +#endif + +#endif /* UI_WIDGET_H */ + -- 2.43.5