]> uap-core.de Git - mizunara.git/commitdiff
update uwproj, ucx and toolkit
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 26 May 2025 19:31:49 +0000 (21:31 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 26 May 2025 19:31:49 +0000 (21:31 +0200)
46 files changed:
configure
make/project.xml
ucx/README [deleted file]
ucx/array.c [deleted file]
ucx/avl.c [deleted file]
ucx/logging.c [deleted file]
ucx/stack.c [deleted file]
ucx/test.c [deleted file]
ucx/utils.c [deleted file]
ui/Makefile
ui/common/condvar.c
ui/common/context.c
ui/common/context.h
ui/common/document.c
ui/common/objs.mk
ui/common/properties.c
ui/common/threadpool.c
ui/common/types.c
ui/gtk/button.c
ui/gtk/container.c
ui/gtk/container.h
ui/gtk/image.c
ui/gtk/image.h
ui/gtk/list.c
ui/gtk/list.h
ui/gtk/menu.c
ui/gtk/menu.h
ui/gtk/model.c [deleted file]
ui/gtk/model.h [deleted file]
ui/gtk/objs.mk
ui/gtk/text.c
ui/gtk/text.h
ui/gtk/toolkit.c
ui/gtk/toolkit.h
ui/gtk/tree.c [deleted file]
ui/gtk/tree.h [deleted file]
ui/gtk/webview.h
ui/gtk/widget.c [new file with mode: 0644]
ui/gtk/widget.h [moved from ucx/ucx.c with 59% similarity]
ui/ui/container.h
ui/ui/display.h
ui/ui/image.h
ui/ui/toolkit.h
ui/ui/tree.h
ui/ui/ui.h
ui/ui/widget.h [new file with mode: 0644]

index c8bd057cb62d4162f73b514a328fbe5cba5f8b89..3f1227fe6164333f74428c7b737de721c06eef2d 100755 (executable)
--- 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
index 21adfa378630458328828ca6ec26c058232bf5fc..b2553d3a798c361077da0cd9eb8a1557c02d2381 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://unixwork.de/uwproj">
+<project version="0.3" xmlns="http://unixwork.de/uwproj">
        <dependency>
                <lang>c</lang>
        </dependency>
        <dependency platform="macos">
                <make>OBJ_EXT = .o</make>
                <make>LIB_EXT = .a</make>
+               <make>LIB_PREFIX = lib</make>
                <make>PACKAGE_SCRIPT = package_osx.sh</make>
        </dependency>
        <dependency platform="unix" not="macos">
                <make>OBJ_EXT = .o</make>
                <make>LIB_EXT = .a</make>
+               <make>LIB_PREFIX = lib</make>
                <make>PACKAGE_SCRIPT = package_unix.sh</make>
        </dependency>
        
diff --git a/ucx/README b/ucx/README
deleted file mode 100644 (file)
index d0890d0..0000000
+++ /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 (file)
index 0592fc6..0000000
+++ /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 <string.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#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 (file)
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 <limits.h>
-
-#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 (file)
index d6fdce0..0000000
+++ /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 <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-
-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 (file)
index 467233e..0000000
+++ /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 <string.h>
-
-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 (file)
index 20b80b4..0000000
+++ /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 (file)
index 101c33f..0000000
+++ /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 <math.h>
-#include <stdio.h>
-#include <limits.h>
-#include <errno.h>
-
-/* 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;
-}
index a0d11482c5f3ef788e4e2e1da597baf3c63e57d8..6905986c4862966add9767c8f85f99b28c33291b 100644 (file)
@@ -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) $<
 
index c61e70c55bc9a115216c9db819dcdd5c275207a7..16f49549932d89fb1725d474eaa360961756a425 100644 (file)
@@ -26,6 +26,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef _WIN32
+
 #include "condvar.h"
 
 #include <stdlib.h>
@@ -68,3 +70,5 @@ void ui_condvar_destroy(UiCondVar *var) {
     free(p);
     
 }
+
+#endif
\ No newline at end of file
index f7dfdd363107e876f80b396f2e8f0f4485fec032..db9d85038c902dca47d72188717d219269bf86cb 100644 (file)
@@ -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);
 }
 
index fdcd6a886d679ac2ee621d69681d60bf02e439de..e0ce3deb9cb2430ddbd012c605bf8f8ce1512b0a 100644 (file)
 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);
index d36014dc5d14d4e506be2b72664863b637db4567..07177bdbfc47d63228df23d5426d6dd7a4beee7f 100644 (file)
@@ -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);
     
index b8980afb1239382950af4fd493c2e712b96876ea..bc1b385abe8977a874d0a26749ce68566879aced 100644 (file)
 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)
 
index 45a9b4e80deeb9c61e81e18484da43ac3db09dcb..629112f7777380f46d86f3503447814b17e6518a 100644 (file)
 #include <sys/stat.h>
 #include <errno.h>
 
+#ifdef _WIN32
+#include <direct.h>
+#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
index d3725c999da419b67df12b2590cf23a1f49c4ba8..7a37407621b98d8556fd53120accb700c1df605d 100644 (file)
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef _WIN32
+
 #include "threadpool.h"
 #include "context.h"
 
-#ifndef _WIN32
-
 #include <pthread.h>
 #include <stdio.h>
 #include <string.h>
index 30cbd034d659970e88a57da792a374e9baed0976..a704b8593f5e18198499207167e44a9035415c6e 100644 (file)
@@ -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;
+}
index 5123e2736c98d6658e34441d67594dde297a2f51..b26a13056e951a3e8c88a13f9546ee396b612fa1 100644 (file)
@@ -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);    
 }
 
index 8a1697732bf9b7b06432c87c9172915ea9edbc72..41c7924c45fec35c37547057e59aa9b53260e6a0 100644 (file)
@@ -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) {
index 421451da04f82aa233c1e08366734fe43547ea79..b39a3e4f4b485cd5368d98ecd0ceedb0e97ee831 100644 (file)
 
 #include <cx/allocator.h>
 #include <cx/hash_map.h>
+#include <cx/list.h>
+#include <cx/array_list.h>
 
 #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);
 
 
index 3c2a0e6498f631c6e30609233d9c5ca79b21f4ee..ea2f6bbaa7d988500c45a8d0dd218b31f4c43024 100644 (file)
 #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
index 00c205d89d71eb5d7ec57a6ddbda39b962435619..987c73b3262d872626d59e30197d4d427bb80b9b 100644 (file)
 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
index aac751f97e1e1094694a689545a7f93376ac891e..978ab532780fcf7e823a7b11754a6cabeda3d8f8 100644 (file)
@@ -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;i<nelm;i++) {
+        listview->elements[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;i<nelm;i++) {
+        ObjWrapper *obj = obj_wrapper_new(elm[i]);
+        g_list_store_append(liststore, obj);
+    }
+}
+
 void ui_listview_update2(UiList *list, int i) {
     UiListView *view = list->obj;
-    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;i<model->columns;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;i<columns;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);
-            }
+            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;i<nelm;i++) {
+            GtkTreeIter iter;
+            GValue value = G_VALUE_INIT;
+            gtk_list_store_insert(listmodel, &iter, -1);
+            g_value_init(&value, G_TYPE_STRING);
+            g_value_set_string(&value, uicbox->elements[i]);
+            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;i<v->nelm;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;i<v->nelm;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);
index 1ff4029e3acd52ab23dd392bf97d2691030b7025..784e6447247cc814ecbdf21e625173499bba5aa4 100644 (file)
@@ -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);
index 085c4db00dd4e5ed09145c16bfbdaf04539e7d26..a54a34b86b20b4e74accbe5e724eb0c1acfb4f59 100644 (file)
@@ -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;i<list->oldcount;i++) {
         g_menu_remove(list->menu, list->index);
index b8bff034d3b4485092d774689cf5faca53dbfa0e..dc6240cdb55dd409c929150f09748bdb410ec600 100644 (file)
@@ -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 (file)
index 6db4e61..0000000
+++ /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 <stdio.h>
-#include <stdlib.h>
-
-#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;i<info->columns;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 (file)
index e237826..0000000
+++ /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 */
index ed89f4d41a47e5010192bd3831c457ab0f66f892..abad5a0679c8ab7a816f25b1260ab08ae5bbcffe 100644 (file)
@@ -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)
index 39f4a6b07132cdc18b751371b605ac579dc7be10..475543c765bb96281d903212e22dab81185cc04f 100644 (file)
@@ -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);
 }
index f7b8ad7c3417149e3f7ed3523a2d5b4ed4f3d488..b7fbda499a8a2267190825bec206524eeeea1ea8 100644 (file)
@@ -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);
index 19a099092a589bad2c51bd65b08591b7986c90c9..7e8e3a5864999417dd8bfa3975fca814be62557d 100644 (file)
@@ -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 */
 }
index 62e82bcc3fc5951ef54172f1baa0b3d4e0578905..b98bb25fd6b3187b87e55af753e9ed57275290db 100644 (file)
@@ -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 (file)
index d93a6f0..0000000
+++ /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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-
-#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;i<model->columns;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;i<nelm;i++) {
-        targets[i].target = str[i];
-    }
-    return targets;
-}
-
-void ui_table_dragsource(UIWIDGET tablewidget, int actions, char *target0, ...) { 
-    va_list ap;
-    va_start(ap, target0);
-    int nelm;
-    char **targets = targets2array(target0, ap, &nelm);
-    va_end(ap);
-    ui_table_dragsource_a(tablewidget, actions, targets, nelm);
-    free(targets);
-}
-
-void ui_table_dragsource_a(UIWIDGET tablewidget, int actions, char **targets, int nelm) {
-    GtkTargetEntry* t = targetstr2gtktargets(targets, nelm);
-    gtk_tree_view_enable_model_drag_source(
-            GTK_TREE_VIEW(ui_get_tree_widget(tablewidget)),
-            GDK_BUTTON1_MASK,
-            t,
-            nelm,
-            GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK);
-    free(t);
-}
-
-void ui_table_dragdest(UIWIDGET tablewidget, int actions, char *target0, ...) {
-    va_list ap;
-    va_start(ap, target0);
-    int nelm;
-    char **targets = targets2array(target0, ap, &nelm);
-    va_end(ap);
-    ui_table_dragdest_a(tablewidget, actions, targets, nelm);
-    free(targets);
-}
-
-void ui_table_dragdest_a(UIWIDGET tablewidget, int actions, char **targets, int nelm) {
-    GtkTargetEntry* t = targetstr2gtktargets(targets, nelm);
-    gtk_tree_view_enable_model_drag_dest(
-            GTK_TREE_VIEW(ui_get_tree_widget(tablewidget)),
-            t,
-            nelm,
-            GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK);
-    free(t);
-}
-
-void ui_listview_update(UiList *list, int i) {
-    UiListView *view = list->obj;
-    UiListModel *model = ui_list_model_new(view->obj, view->var, view->model);
-    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 (file)
index e771ea1..0000000
+++ /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 */
-
index c06cfe2e70cb91b0470e9c7b13649dce33eecdd8..e3508589a07b47fbbb40ee4d18b77f70ec027727 100644 (file)
 #ifdef UI_WEBVIEW
 
 #include "../ui/webview.h"
+
+#if GTK_MAJOR_VERSION >= 4
 #include <webkit/webkit.h>
+#else
+#include <webkit2/webkit2.h>
+#endif
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/ui/gtk/widget.c b/ui/gtk/widget.c
new file mode 100644 (file)
index 0000000..505bca6
--- /dev/null
@@ -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);
+}
similarity index 59%
rename from ucx/ucx.c
rename to ui/gtk/widget.h
index 923330d11748654a6cb520fe9ca33813bcf070f3..57c7314d3cc2fe91290817fd6092b73a36e74a23 100644 (file)
--- a/ucx/ucx.c
@@ -1,24 +1,7 @@
-/**
- * @mainpage UAP Common Extensions
- * Library with common and useful functions, macros and data structures.
- * <p>
- * Latest available source:<br>
- * <a href="https://sourceforge.net/projects/ucx/files/">
- * https://sourceforge.net/projects/ucx/files/</a>
- * </p>
- * 
- * <p>
- * Repositories:<br>
- * <a href="https://sourceforge.net/p/ucx/code">
- * https://sourceforge.net/p/ucx/code</a>
- * -&nbsp;or&nbsp;-
- * <a href="https://develop.uap-core.de/hg/ucx">
- * https://develop.uap-core.de/hg/ucx</a>
- * </p>
- * 
- * <h2>LICENCE</h2>
- * 
- * 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:
  * 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 */
 
index c7d6523431d8e7ee23f86d1af5e918ded3b30047..3f828ea6b219c9b598917fda72da67254a5b6198 100644 (file)
@@ -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);
index 58b4c8476b09f5fa2b347d6039929d3bb7c0ad58..8ed861b6e9030e836ed9e61bd3ee6dc4dc65ab81 100644 (file)
@@ -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 */
 
index 94ab8a6961db9e412acebf8cd04afbf70e4de739..7217caa5185e995c5375d4e4dd057d285dba3ed4 100644 (file)
@@ -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
 }
index dd4a7d1522fdc4097b215b8a1efa640da6ec1968..2af07c5406e381b78976b793aae3dea8ece61a8c 100644 (file)
@@ -30,6 +30,7 @@
 #define        UI_TOOLKIT_H
 
 #include <inttypes.h>
+#include <stdlib.h>
 
 #ifdef UI_COCOA
 
@@ -60,24 +61,41 @@ typedef void* UIMENU;   // NSMenu*
 #include <adwaita.h>
 #endif
 
-#elif UI_MOTIF
-
-#include <Xm/XmAll.h> 
-#define UIWIDGET Widget
-#define UIMENU   Widget
-
 #elif defined(UI_QT4) || defined(UI_QT5)
+#define UI_QT
+
 #ifdef __cplusplus
+
 #include <QApplication>
 #include <QWidget>
 #include <QMenu>
+
 #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 <Xm/XmAll.h> 
+#define UIWIDGET Widget
+#define UIMENU   Widget
+
+
+#elif UI_WIN32
+
+#include <Windows.h>
+
+#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
index 589d2a1fddd10daff42f17e080a61f06f2e92801..8217cf601512b023957c91817430d0cbfbc483d1 100644 (file)
@@ -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);
 
 
index 41883f13926c47aaa0e5cb843b9709648489f930..57c802593c1b7b261ffbe242afd86a711789b6a2 100644 (file)
@@ -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 (file)
index 0000000..e08233d
--- /dev/null
@@ -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 */
+