update toolkit

Sun, 09 Jun 2024 15:43:08 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 09 Jun 2024 15:43:08 +0200
changeset 32
e5f4d8af567e
parent 31
bf810176ddb8
child 33
cdeb0dc47ef5

update toolkit

application/Makefile file | annotate | diff | comparison | revisions
configure file | annotate | diff | comparison | revisions
make/Makefile.mk file | annotate | diff | comparison | revisions
make/configure.vm file | annotate | diff | comparison | revisions
make/osx.mk file | annotate | diff | comparison | revisions
make/project.xml file | annotate | diff | comparison | revisions
make/toolchain.sh file | annotate | diff | comparison | revisions
make/uwproj.xsd file | annotate | diff | comparison | revisions
make/windows.mk file | annotate | diff | comparison | revisions
ucx/Makefile file | annotate | diff | comparison | revisions
ui/common/objs.mk file | annotate | diff | comparison | revisions
ui/common/threadpool.c file | annotate | diff | comparison | revisions
ui/common/threadpool.h file | annotate | diff | comparison | revisions
ui/gtk/button.c file | annotate | diff | comparison | revisions
ui/gtk/button.h file | annotate | diff | comparison | revisions
ui/gtk/container.c file | annotate | diff | comparison | revisions
ui/gtk/container.h file | annotate | diff | comparison | revisions
ui/gtk/display.c file | annotate | diff | comparison | revisions
ui/gtk/display.h file | annotate | diff | comparison | revisions
ui/gtk/dnd.c file | annotate | diff | comparison | revisions
ui/gtk/entry.c file | annotate | diff | comparison | revisions
ui/gtk/entry.h file | annotate | diff | comparison | revisions
ui/gtk/graphics.c file | annotate | diff | comparison | revisions
ui/gtk/image.c file | annotate | diff | comparison | revisions
ui/gtk/image.h file | annotate | diff | comparison | revisions
ui/gtk/menu.c file | annotate | diff | comparison | revisions
ui/gtk/menu.h file | annotate | diff | comparison | revisions
ui/gtk/objs.mk file | annotate | diff | comparison | revisions
ui/gtk/range.c file | annotate | diff | comparison | revisions
ui/gtk/text.c file | annotate | diff | comparison | revisions
ui/gtk/text.h file | annotate | diff | comparison | revisions
ui/gtk/toolbar.c file | annotate | diff | comparison | revisions
ui/gtk/toolbar.h file | annotate | diff | comparison | revisions
ui/gtk/toolkit.c file | annotate | diff | comparison | revisions
ui/gtk/toolkit.h file | annotate | diff | comparison | revisions
ui/gtk/tree.c file | annotate | diff | comparison | revisions
ui/gtk/tree.h file | annotate | diff | comparison | revisions
ui/gtk/window.c file | annotate | diff | comparison | revisions
ui/ui/display.h file | annotate | diff | comparison | revisions
ui/ui/entry.h file | annotate | diff | comparison | revisions
ui/ui/text.h file | annotate | diff | comparison | revisions
ui/ui/toolkit.h file | annotate | diff | comparison | revisions
--- a/application/Makefile	Mon Feb 12 21:13:23 2024 +0100
+++ b/application/Makefile	Sun Jun 09 15:43:08 2024 +0200
@@ -29,17 +29,23 @@
 BUILD_ROOT = ..
 include ../config.mk
 
-CFLAGS += -I../ui/ -I../ucx
+CFLAGS += -I../ui/ -I../ucx -I..
 
-SRC = main.c
+SRC  = main.c
+SRC += application.c
+SRC += config.c
+SRC += davcontroller.c
+SRC += pwd.c
+SRC += system.c
+SRC += window.c
 
 OBJ = $(SRC:%.c=../build/application/%.$(OBJ_EXT))
 
 all: ../build/bin/mk12
 
 ../build/bin/mk12: $(OBJ) $(BUILD_ROOT)/build/lib/libuitk.a
-	$(LD) -o ../build/bin/mk12$(APP_EXT) $(OBJ) -L$(BUILD_ROOT)/build/lib -luitk -lucx $(LDFLAGS) $(TK_LDFLAGS)
+	$(CC) -o ../build/bin/mk12$(APP_EXT) $(OBJ) -L$(BUILD_ROOT)/build/lib -luitk -lucx -lidav $(LDFLAGS) $(TK_LDFLAGS) $(DAV_LDFLAGS)
 
 ../build/application/%.$(OBJ_EXT): %.c
-	$(CC) ../ucx $(CFLAGS) $(TK_CFLAGS) -o $@ -c $<
+	$(CC) ../ucx $(CFLAGS) $(TK_CFLAGS) $(DAV_CFLAGS) -o $@ -c $<
 
--- a/configure	Mon Feb 12 21:13:23 2024 +0100
+++ b/configure	Sun Jun 09 15:43:08 2024 +0200
@@ -1,40 +1,53 @@
 #!/bin/sh
 
-
-PREFIX=/usr
-EPREFIX=$PREFIX
+# create temporary directory
+TEMP_DIR=".tmp-`uname -n`"
+rm -Rf "$TEMP_DIR"
+if mkdir -p "$TEMP_DIR"; then
+    :
+else
+    echo "Cannot create tmp dir $TEMP_DIR"
+    echo "Abort"
+    exit 1
+fi
+touch "$TEMP_DIR/options"
+touch "$TEMP_DIR/features"
 
-BINDIR=
-SBINDIR=
-LIBDIR=
-LIBEXECDIR=
-DATADIR=
-SYSCONFDIR=
-SHAREDSTATEDIR=
-LOCALSTATEDIR=
-INCLUDEDIR=
-INFODIR=
-MANDIR=
+# define standard variables
+# also define standard prefix (this is where we will search for config.site)
+prefix=/usr
+exec_prefix=
+bindir=
+sbindir=
+libdir=
+libexecdir=
+datarootdir=
+datadir=
+sysconfdir=
+sharedstatedir=
+localstatedir=
+runstatedir=
+includedir=
+infodir=
+localedir=
+mandir=
 
-OS=`uname -s`
-OS_VERSION=`uname -r`
-
-TEMP_DIR=".tmp-`uname -n`"
-mkdir -p $TEMP_DIR
-if [ $? -ne 0 ]; then
-	echo "Cannot create tmp dir"
-	echo "Abort"
-fi
-touch $TEMP_DIR/options
-touch $TEMP_DIR/features
+# custom variables
 
 # features
 
+# clean abort
+abort_configure()
+{
+    rm -Rf "$TEMP_DIR"
+    exit 1
+}
+
 # help text
 printhelp()
 {
-	echo "Usage: $0 [OPTIONS]..."
-	cat << __EOF__
+    echo "Usage: $0 [OPTIONS]..."
+    cat << __EOF__
 Installation directories:
   --prefix=PREFIX         path prefix for architecture-independent files
                           [/usr]
@@ -47,497 +60,692 @@
   --sysconfdir=DIR        system configuration files [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       run-time variable data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
   --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
   --infodir=DIR           info documentation [DATAROOTDIR/info]
   --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
 
 Options:
+  --debug                 add extra compile flags for debug builds
+  --release               add extra compile flags for release builds
   --toolkit=(gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|motif)
 
 __EOF__
 }
 
 #
-# parse arguments 
+# parse arguments
 #
-for ARG in $@
+BUILD_TYPE="default"
+for ARG in "$@"
 do
     case "$ARG" in
-		"--prefix="*)         PREFIX=${ARG#--prefix=} ;;
-		"--exec-prefix="*)    EPREFIX=${ARG#--exec-prefix=} ;;
-		"--bindir="*)         BINDIR=${ARG#----bindir=} ;;
-		"--sbindir="*)        SBINDIR=${ARG#--sbindir=} ;;
-		"--libdir="*)         LIBDIR=${ARG#--libdir=} ;;
-		"--libexecdir="*)     LIBEXECDIR=${ARG#--libexecdir=} ;;
-		"--datadir="*)        DATADIR=${ARG#--datadir=} ;;
-		"--sysconfdir="*)     SYSCONFDIR=${ARG#--sysconfdir=} ;;
-		"--sharedstatedir="*) SHAREDSTATEDIR=${ARG#--sharedstatedir=} ;;
-		"--localstatedir="*)  LOCALSTATEDIR=${ARG#--localstatedir=} ;;
-		"--includedir="*)     INCLUDEDIR=${ARG#--includedir=} ;;
-		"--infodir="*)        INFODIR=${ARG#--infodir=} ;;
-		"--mandir"*)          MANDIR=${ARG#--mandir} ;;
-		"--help"*) printhelp; exit 1 ;;
-    	"--toolkit="*) OPT_TOOLKIT=${ARG#--toolkit=} ;;
-		"-"*) echo "unknown option: $ARG"; exit 1 ;;
-	esac
+        "--prefix="*)         prefix=${ARG#--prefix=} ;;
+        "--exec-prefix="*)    exec_prefix=${ARG#--exec-prefix=} ;;
+        "--bindir="*)         bindir=${ARG#----bindir=} ;;
+        "--sbindir="*)        sbindir=${ARG#--sbindir=} ;;
+        "--libdir="*)         libdir=${ARG#--libdir=} ;;
+        "--libexecdir="*)     libexecdir=${ARG#--libexecdir=} ;;
+        "--datarootdir="*)    datarootdir=${ARG#--datarootdir=} ;;
+        "--datadir="*)        datadir=${ARG#--datadir=} ;;
+        "--sysconfdir="*)     sysconfdir=${ARG#--sysconfdir=} ;;
+        "--sharedstatedir="*) sharedstatedir=${ARG#--sharedstatedir=} ;;
+        "--localstatedir="*)  localstatedir=${ARG#--localstatedir=} ;;
+        "--includedir="*)     includedir=${ARG#--includedir=} ;;
+        "--infodir="*)        infodir=${ARG#--infodir=} ;;
+        "--mandir"*)          mandir=${ARG#--mandir} ;;
+        "--localedir"*)       localedir=${ARG#--localedir} ;;
+        "--help"*) printhelp; abort_configure ;;
+        "--debug")           BUILD_TYPE="debug" ;;
+        "--release")         BUILD_TYPE="release" ;;
+        "--toolkit="*) OPT_TOOLKIT=${ARG#--toolkit=} ;;
+        "-"*) echo "unknown option: $ARG"; abort_configure ;;
+    esac
 done
 
-# set dir variables
-if [ -z "$BINDIR" ]; then
-	BINDIR=$EPREFIX/bin
-fi
-if [ -z "$SBINDIR" ]; then
-	SBINDIR=$EPREFIX/sbin
-fi
-if [ -z "$LIBDIR" ]; then
-	LIBDIR=$EPREFIX/lib
-fi
-if [ -z "$LIBEXEC" ]; then
-	LIBEXECDIR=$EPREFIX/libexec
-fi
-if [ -z "$DATADIR" ]; then
-	DATADIR=$PREFIX/share
-fi
-if [ -z "$SYSCONFDIR" ]; then
-	SYSCONFDIR=$PREFIX/etc
-fi
-if [ -z "$SHAREDSTATEDIR" ]; then
-	SHAREDSTATEDIR=$PREFIX/com
-fi
-if [ -z "$LOCALSTATEDIR" ]; then
-	LOCALSTATEDIR=$PREFIX/var
-fi
-if [ -z "$INCLUDEDIR" ]; then
-	INCLUDEDIR=$PREFIX/include
-fi
-if [ -z "$INFODIR" ]; then
-	INFODIR=$PREFIX/info
-fi
-if [ -z "$MANDIR" ]; then
-	MANDIR=$PREFIX/man
+
+
+# set defaults for dir variables
+: ${exec_prefix:="$prefix"}
+: ${bindir:='${exec_prefix}/bin'}
+: ${sbindir:='${exec_prefix}/sbin'}
+: ${libdir:='${exec_prefix}/lib'}
+: ${libexecdir:='${exec_prefix}/libexec'}
+: ${datarootdir:='${prefix}/share'}
+: ${datadir:='${datarootdir}'}
+: ${sysconfdir:='${prefix}/etc'}
+: ${sharedstatedir:='${prefix}/com'}
+: ${localstatedir:='${prefix}/var'}
+: ${runstatedir:='${localstatedir}/run'}
+: ${includedir:='${prefix}/include'}
+: ${infodir:='${datarootdir}/info'}
+: ${mandir:='${datarootdir}/man'}
+: ${localedir:='${datarootdir}/locale'}
+
+# check if a config.site exists and load it
+if [ -n "$CONFIG_SITE" ]; then
+    # CONFIG_SITE may contain space separated file names
+    for cs in $CONFIG_SITE; do
+        printf "loading defaults from $cs... "
+        . "$cs"
+        echo ok
+    done
+elif [ -f "$prefix/share/config.site" ]; then
+    printf "loading site defaults... "
+    . "$prefix/share/config.site"
+    echo ok
+elif [ -f "$prefix/etc/config.site" ]; then
+    printf "loading site defaults... "
+    . "$prefix/etc/config.site"
+    echo ok
 fi
 
-which pkg-config > /dev/null
-if [ $? -eq 0 ]; then
-    PKG_CONFIG=pkg-config
-else
-    PKG_CONFIG=false
-fi
+# Test for availability of pkg-config
+PKG_CONFIG=`command -v pkg-config`
+: ${PKG_CONFIG:="false"}
 
 # Simple uname based platform detection
 # $PLATFORM is used for platform dependent dependency selection
+OS=`uname -s`
+OS_VERSION=`uname -r`
 printf "detect platform... "
-if [ $OS = SunOS ]; then
+if [ "$OS" = "SunOS" ]; then
     PLATFORM="solaris sunos unix svr4"
-fi
-if [ $OS = Linux ]; then
+elif [ "$OS" = "Linux" ]; then
     PLATFORM="linux unix"
-fi
-if [ $OS = FreeBSD ]; then
+elif [ "$OS" = "FreeBSD" ]; then
     PLATFORM="freebsd bsd unix"
-fi
-if [ $OS = Darwin ]; then
+elif [ "$OS" = "OpenBSD" ]; then
+    PLATFORM="openbsd bsd unix"
+elif [ "$OS" = "NetBSD" ]; then
+    PLATFORM="netbsd bsd unix"
+elif [ "$OS" = "Darwin" ]; then
     PLATFORM="macos osx bsd unix"
-fi
-echo $OS | grep "MINGW" > /dev/null
-if [ $? -eq 0 ]; then
+elif echo "$OS" | grep -i "MINGW" > /dev/null; then
     PLATFORM="windows mingw"
 fi
-
-if [ -z "$PLATFORM" ]; then
-    PLATFORM="unix"
-fi
+: ${PLATFORM:="unix"}
 
-for p in $PLATFORM
-do
-	PLATFORM_NAME=$p
-	break
-done
-echo $PLATFORM_NAME
+PLATFORM_NAME=`echo "$PLATFORM" | cut -f1 -d' ' -`
+echo "$PLATFORM_NAME"
 
 isplatform()
 {
     for p in $PLATFORM
     do
-        if [ $p = $1 ]; then
+        if [ "$p" = "$1" ]; then
             return 0
         fi
     done
     return 1
 }
-isnotplatform()
+notisplatform()
 {
     for p in $PLATFORM
     do
-        if [ $p = $1 ]; then
+        if [ "$p" = "$1" ]; then
+            return 1
+        fi
+    done
+    return 0
+}
+istoolchain()
+{
+    for t in $TOOLCHAIN
+    do
+        if [ "$t" = "$1" ]; then
+            return 0
+        fi
+    done
+    return 1
+}
+notistoolchain()
+{
+    for t in $TOOLCHAIN
+    do
+        if [ "$t" = "$1" ]; then
             return 1
         fi
     done
     return 0
 }
 
-# generate config.mk and config.h
-cat > $TEMP_DIR/config.mk << __EOF__
-#
-# config.mk generated by configure
-#
 
-# general vars
-
-PREFIX=$PREFIX
-EPREFIX=$EPREFIX
-
-BINDIR=$BINDIR
-SBINDIR=$SBINDIR
-LIBDIR=$LIBDIR
-LIBEXECDIR=$LIBEXECDIR
-DATADIR=$DATADIR
-SYSCONFDIR=$SYSCONFDIR
-SHAREDSTATEDIR=$SHAREDSTATEDIR
-LOCALSTATEDIR=$LOCALSTATEDIR
-INCLUDEDIR=$INCLUDEDIR
-INFODIR=$INFODIR
-MANDIR=$MANDIR
-
+# generate vars.mk
+cat > "$TEMP_DIR/vars.mk" << __EOF__
+prefix=$prefix
+exec_prefix=$exec_prefix
+bindir=$bindir
+sbindir=$sbindir
+libdir=$libdir
+libexecdir=$libexecdir
+datarootdir=$datarootdir
+datadir=$datadir
+sysconfdir=$sysconfdir
+sharedstatedir=$sharedstatedir
+localstatedir=$localstatedir
+runstatedir=$runstatedir
+includedir=$includedir
+infodir=$infodir
+mandir=$mandir
+localedir=$localedir
 __EOF__
 
-echo > $TEMP_DIR/make.mk
-
-ENV_CFLAGS=$CFLAGS
-ENV_LDFLAGS=$LDFLAGS
-ENV_CXXFLAGS=$CXXFLAGS
-
-# Toolchain detection
-# this will insert make vars to config.mk
+# toolchain detection utilities
 . make/toolchain.sh
 
-# add user specified flags to config.mk
-echo >> $TEMP_DIR/config.mk
-if [ ! -z "${ENV_CFLAGS}" ]; then
-    echo "CFLAGS += $ENV_CFLAGS" >> $TEMP_DIR/config.mk
-fi
-if [ ! -z "${ENV_CXXFLAGS}" ]; then
-    echo "CXXFLAGS += $ENV_CXXFLAGS" >> $TEMP_DIR/config.mk
-fi
-if [ ! -z "${ENV_LDFLAGS}" ]; then
-    echo "LDFLAGS += $ENV_LDFLAGS" >> $TEMP_DIR/config.mk
-fi
-
 #
 # DEPENDENCIES
 #
 
-dependency_qt4()
+# check languages
+lang_c=
+lang_cpp=
+if detect_c_compiler ; then
+    lang_c=1
+fi
+
+# create buffer for make variables required by dependencies
+echo > "$TEMP_DIR/make.mk"
+
+test_pkg_config()
 {
-    printf "checking for qt4... "
-    # dependency qt4 
+    if "$PKG_CONFIG" --exists "$1" ; then :
+    else return 1 ; fi
+    if [ -z "$2" ] || "$PKG_CONFIG" --atleast-version="$2" "$1" ; then :
+    else return 1 ; fi
+    if [ -z "$3" ] || "$PKG_CONFIG" --exact-version="$3" "$1" ; then :
+    else return 1 ; fi
+    if [ -z "$4" ] || "$PKG_CONFIG" --max-version="$4" "$1" ; then :
+    else return 1 ; fi
+    return 0
+}
+
+print_check_msg()
+{
+    if [ -z "$1" ]; then
+        shift
+        printf "$@"
+    fi
+}
+
+dependency_error_gtk2legacy()
+{
+    print_check_msg "$dep_checked_gtk2legacy" "checking for gtk2legacy... "
+    # dependency gtk2legacy
     while true
     do
-        qmake-qt4 -o - /dev/null | grep DEFINES\  > /dev/null
-        if [ $? -eq 0 ]; then
-            CFLAGS="$CFLAGS `qmake-qt4 -o - /dev/null | grep DEFINES\ `"
-        else
+        if [ -z "$PKG_CONFIG" ]; then
             break
         fi
-        qmake-qt4 -o - /dev/null | grep INCPATH\  > /dev/null
-        if [ $? -eq 0 ]; then
-            CFLAGS="$CFLAGS `qmake-qt4 -o - /dev/null | grep INCPATH\ `"
+        if test_pkg_config "gtk+-2.0" "" "" "" ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-2.0`"
+            TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-2.0`"
         else
             break
         fi
-         > /dev/null
-        if [ $? -eq 0 ]; then
-            LDFLAGS="$LDFLAGS ``"
+        TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK2 -DUI_GTK2LEGACY"
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
+        print_check_msg "$dep_checked_gtk2legacy" "yes\n"
+        dep_checked_gtk2legacy=1
+        return 1
+    done
+
+    print_check_msg "$dep_checked_gtk2legacy" "no\n"
+    dep_checked_gtk2legacy=1
+    return 0
+}
+dependency_error_curl()
+{
+    print_check_msg "$dep_checked_curl" "checking for curl... "
+    # dependency curl platform="macos"
+    while true
+    do
+        if notisplatform "macos"; then
+            break
+        fi
+        if tmp_flags=`curl-config --cflags` ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS $tmp_flags"
         else
             break
         fi
-        which qmake-qt4 > /dev/null
-        if [ $? -ne 0 ]; then
-        	break
+        if tmp_flags=`curl-config --ldflags` ; then
+            TEMP_LDFLAGS="$TEMP_LDFLAGS $tmp_flags"
+        else
+            break
         fi
-		echo yes
-        return 0
+        print_check_msg "$dep_checked_curl" "yes\n"
+        dep_checked_curl=1
+        return 1
     done
-	
-	echo no
-	return 1
-}
-dependency_gtk2legacy()
-{
-    printf "checking for gtk2legacy... "
-    # dependency gtk2legacy 
+
+    # dependency curl
     while true
     do
         if [ -z "$PKG_CONFIG" ]; then
-        	break
+            break
         fi
-		$PKG_CONFIG gtk+-2.0
-        if [ $? -ne 0 ] ; then
+        if test_pkg_config "libcurl" "" "" "" ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags libcurl`"
+            TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs libcurl`"
+        else
             break
         fi
-        CFLAGS="$CFLAGS `$PKG_CONFIG --cflags gtk+-2.0`"
-        LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs gtk+-2.0`"
-        CFLAGS="$CFLAGS -DUI_GTK2 -DUI_GTK2LEGACY"    
-        LDFLAGS="$LDFLAGS -lpthread"    
-		echo yes
-        return 0
+        print_check_msg "$dep_checked_curl" "yes\n"
+        dep_checked_curl=1
+        return 1
     done
-	
-	echo no
-	return 1
-}
-dependency_qt5()
-{
-    printf "checking for qt5... "
-    # dependency qt5 
+
+    # dependency curl
     while true
     do
-        qmake-qt5 -o - /dev/null | grep DEFINES\  > /dev/null
-        if [ $? -eq 0 ]; then
-            CFLAGS="$CFLAGS `qmake-qt5 -o - /dev/null | grep DEFINES\ `"
+        if tmp_flags=`curl-config --cflags` ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS $tmp_flags"
+        else
+            break
+        fi
+        if tmp_flags=`curl-config --libs` ; then
+            TEMP_LDFLAGS="$TEMP_LDFLAGS $tmp_flags"
         else
             break
         fi
-        qmake-qt5 -o - /dev/null | grep INCPATH\  > /dev/null
-        if [ $? -eq 0 ]; then
-            CFLAGS="$CFLAGS `qmake-qt5 -o - /dev/null | grep INCPATH\ `"
+        print_check_msg "$dep_checked_curl" "yes\n"
+        dep_checked_curl=1
+        return 1
+    done
+
+    print_check_msg "$dep_checked_curl" "no\n"
+    dep_checked_curl=1
+    return 0
+}
+dependency_error_gtk2()
+{
+    print_check_msg "$dep_checked_gtk2" "checking for gtk2... "
+    # dependency gtk2
+    while true
+    do
+        if [ -z "$PKG_CONFIG" ]; then
+            break
+        fi
+        if pkg-config --atleast-version=2.20 gtk+-2.0 > /dev/null ; then
+            :
         else
             break
         fi
-         > /dev/null
-        if [ $? -eq 0 ]; then
-            LDFLAGS="$LDFLAGS ``"
+        if test_pkg_config "gtk+-2.0" "" "" "" ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-2.0`"
+            TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-2.0`"
         else
             break
         fi
-        which qmake-qt5 > /dev/null
-        if [ $? -ne 0 ]; then
-        	break
-        fi
-		echo yes
-        return 0
+        TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK2"
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
+        print_check_msg "$dep_checked_gtk2" "yes\n"
+        dep_checked_gtk2=1
+        return 1
     done
-	
-	echo no
-	return 1
+
+    print_check_msg "$dep_checked_gtk2" "no\n"
+    dep_checked_gtk2=1
+    return 0
 }
-dependency_gtk2()
+dependency_error_gtk3()
 {
-    printf "checking for gtk2... "
-    # dependency gtk2 
+    print_check_msg "$dep_checked_gtk3" "checking for gtk3... "
+    # dependency gtk3
+    while true
+    do
+        if [ -z "$PKG_CONFIG" ]; then
+            break
+        fi
+        if test_pkg_config "gtk+-3.0" "" "" "" ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-3.0`"
+            TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-3.0`"
+        else
+            break
+        fi
+        TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK3"
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
+        print_check_msg "$dep_checked_gtk3" "yes\n"
+        dep_checked_gtk3=1
+        return 1
+    done
+
+    print_check_msg "$dep_checked_gtk3" "no\n"
+    dep_checked_gtk3=1
+    return 0
+}
+dependency_error_gtk4()
+{
+    print_check_msg "$dep_checked_gtk4" "checking for gtk4... "
+    # dependency gtk4
     while true
     do
         if [ -z "$PKG_CONFIG" ]; then
-        	break
+            break
+        fi
+        if test_pkg_config "gtk+-4.0" "" "" "" ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-4.0`"
+            TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-4.0`"
+        else
+            break
         fi
-		$PKG_CONFIG gtk+-2.0
-        if [ $? -ne 0 ] ; then
+        TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK3"
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
+        print_check_msg "$dep_checked_gtk4" "yes\n"
+        dep_checked_gtk4=1
+        return 1
+    done
+
+    print_check_msg "$dep_checked_gtk4" "no\n"
+    dep_checked_gtk4=1
+    return 0
+}
+dependency_error_openssl()
+{
+    print_check_msg "$dep_checked_openssl" "checking for openssl... "
+    # dependency openssl platform="windows"
+    while true
+    do
+        if notisplatform "windows"; then
             break
         fi
-        CFLAGS="$CFLAGS `$PKG_CONFIG --cflags gtk+-2.0`"
-        LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs gtk+-2.0`"
-        CFLAGS="$CFLAGS -DUI_GTK2"    
-        LDFLAGS="$LDFLAGS -lpthread"    
-        pkg-config --atleast-version=2.20 gtk+-2.0 > /dev/null
-        if [ $? -ne 0 ]; then
-        	break
-        fi
-		echo yes
-        return 0
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -lssl -lcrypto"
+        print_check_msg "$dep_checked_openssl" "yes\n"
+        dep_checked_openssl=1
+        return 1
     done
-	
-	echo no
-	return 1
-}
-dependency_gtk3()
-{
-    printf "checking for gtk3... "
-    # dependency gtk3 
+
+    # dependency openssl platform="macos"
     while true
     do
-        if [ -z "$PKG_CONFIG" ]; then
-        	break
-        fi
-		$PKG_CONFIG gtk+-3.0
-        if [ $? -ne 0 ] ; then
+        if notisplatform "macos"; then
             break
         fi
-        CFLAGS="$CFLAGS `$PKG_CONFIG --cflags gtk+-3.0`"
-        LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs gtk+-3.0`"
-        CFLAGS="$CFLAGS -DUI_GTK3"    
-        LDFLAGS="$LDFLAGS -lpthread"    
-		echo yes
-        return 0
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -framework CoreFoundation"
+        print_check_msg "$dep_checked_openssl" "yes\n"
+        dep_checked_openssl=1
+        return 1
     done
-	
-	echo no
-	return 1
-}
-dependency_gtk4()
-{
-    printf "checking for gtk4... "
-    # dependency gtk4 
+
+    # dependency openssl platform="bsd"
+    while true
+    do
+        if notisplatform "bsd"; then
+            break
+        fi
+        if isplatform "macos" || istoolchain "macos"; then
+            break
+        fi
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -lssl -lcrypto"
+        print_check_msg "$dep_checked_openssl" "yes\n"
+        dep_checked_openssl=1
+        return 1
+    done
+
+    # dependency openssl
     while true
     do
         if [ -z "$PKG_CONFIG" ]; then
-        	break
+            break
         fi
-		$PKG_CONFIG gtk+-4.0
-        if [ $? -ne 0 ] ; then
+        if test_pkg_config "openssl" "" "" "" ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags openssl`"
+            TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs openssl`"
+        else
             break
         fi
-        CFLAGS="$CFLAGS `$PKG_CONFIG --cflags gtk+-4.0`"
-        LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs gtk+-4.0`"
-        CFLAGS="$CFLAGS -DUI_GTK3"    
-        LDFLAGS="$LDFLAGS -lpthread"    
-		echo yes
-        return 0
+        print_check_msg "$dep_checked_openssl" "yes\n"
+        dep_checked_openssl=1
+        return 1
     done
-	
-	echo no
-	return 1
+
+    print_check_msg "$dep_checked_openssl" "no\n"
+    dep_checked_openssl=1
+    return 0
 }
-dependency_motif()
+dependency_error_motif()
 {
-    printf "checking for motif... "
+    print_check_msg "$dep_checked_motif" "checking for motif... "
     # dependency motif platform="bsd"
     while true
     do
-    	if isnotplatform "bsd"; then
+        if notisplatform "bsd"; then
             break
         fi
-        CFLAGS="$CFLAGS -DUI_MOTIF -I/usr/local/include/X11"    
-        LDFLAGS="$LDFLAGS -lXm -lXt -lX11 -lpthread"    
-		echo yes
-        return 0
+        TEMP_CFLAGS="$TEMP_CFLAGS -DUI_MOTIF -I/usr/local/include/X11"
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11 -lpthread"
+        print_check_msg "$dep_checked_motif" "yes\n"
+        dep_checked_motif=1
+        return 1
+    done
+
+    # dependency motif
+    while true
+    do
+        TEMP_CFLAGS="$TEMP_CFLAGS -DUI_MOTIF"
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11 -lpthread"
+        print_check_msg "$dep_checked_motif" "yes\n"
+        dep_checked_motif=1
+        return 1
     done
-	
-    # dependency motif 
+
+    print_check_msg "$dep_checked_motif" "no\n"
+    dep_checked_motif=1
+    return 0
+}
+dependency_error_libxml2()
+{
+    print_check_msg "$dep_checked_libxml2" "checking for libxml2... "
+    # dependency libxml2 platform="windows"
+    while true
+    do
+        if notisplatform "windows"; then
+            break
+        fi
+        if tmp_flags=`xml2-config --cflags` ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS $tmp_flags"
+        else
+            break
+        fi
+        if tmp_flags=`xml2-config --libs` ; then
+            TEMP_LDFLAGS="$TEMP_LDFLAGS $tmp_flags"
+        else
+            break
+        fi
+        print_check_msg "$dep_checked_libxml2" "yes\n"
+        dep_checked_libxml2=1
+        return 1
+    done
+
+    # dependency libxml2 platform="macos"
     while true
     do
-        CFLAGS="$CFLAGS -DUI_MOTIF"    
-        LDFLAGS="$LDFLAGS -lXm -lXt -lX11 -lpthread"    
-		echo yes
-        return 0
+        if notisplatform "macos"; then
+            break
+        fi
+        if tmp_flags=`xml2-config --cflags` ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS $tmp_flags"
+        else
+            break
+        fi
+        if tmp_flags=`xml2-config --libs` ; then
+            TEMP_LDFLAGS="$TEMP_LDFLAGS $tmp_flags"
+        else
+            break
+        fi
+        print_check_msg "$dep_checked_libxml2" "yes\n"
+        dep_checked_libxml2=1
+        return 1
+    done
+
+    # dependency libxml2
+    while true
+    do
+        if [ -z "$PKG_CONFIG" ]; then
+            break
+        fi
+        if test_pkg_config "libxml-2.0" "" "" "" ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags libxml-2.0`"
+            TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs libxml-2.0`"
+        else
+            break
+        fi
+        print_check_msg "$dep_checked_libxml2" "yes\n"
+        dep_checked_libxml2=1
+        return 1
     done
-	
-	echo no
-	return 1
+
+    # dependency libxml2
+    while true
+    do
+        if tmp_flags=`xml2-config --cflags` ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS $tmp_flags"
+        else
+            break
+        fi
+        if tmp_flags=`xml2-config --libs` ; then
+            TEMP_LDFLAGS="$TEMP_LDFLAGS $tmp_flags"
+        else
+            break
+        fi
+        print_check_msg "$dep_checked_libxml2" "yes\n"
+        dep_checked_libxml2=1
+        return 1
+    done
+
+    print_check_msg "$dep_checked_libxml2" "no\n"
+    dep_checked_libxml2=1
+    return 0
 }
-dependency_winui()
+dependency_error_cocoa()
 {
-    printf "checking for winui... "
+    print_check_msg "$dep_checked_cocoa" "checking for cocoa... "
+    # dependency cocoa platform="macos"
+    while true
+    do
+        if notisplatform "macos"; then
+            break
+        fi
+        TEMP_CFLAGS="$TEMP_CFLAGS -DUI_COCOA"
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -lobjc -framework Cocoa"
+        print_check_msg "$dep_checked_cocoa" "yes\n"
+        dep_checked_cocoa=1
+        return 1
+    done
+
+    print_check_msg "$dep_checked_cocoa" "no\n"
+    dep_checked_cocoa=1
+    return 0
+}
+dependency_error_winui()
+{
+    print_check_msg "$dep_checked_winui" "checking for winui... "
     # dependency winui platform="windows"
     while true
     do
-    	if isnotplatform "windows"; then
+        if notisplatform "windows"; then
             break
         fi
-        CFLAGS="$CFLAGS -DUI_WINUI"    
-		echo yes
-        return 0
+        TEMP_CFLAGS="$TEMP_CFLAGS -DUI_WINUI"
+        print_check_msg "$dep_checked_winui" "yes\n"
+        dep_checked_winui=1
+        return 1
     done
-	
-	echo no
-	return 1
+
+    print_check_msg "$dep_checked_winui" "no\n"
+    dep_checked_winui=1
+    return 0
 }
-dependency_cocoa()
-{
-    printf "checking for cocoa... "
-    # dependency cocoa platform="macos"
-    while true
-    do
-    	if isnotplatform "macos"; then
-            break
-        fi
-        CFLAGS="$CFLAGS -DUI_COCOA"    
-        LDFLAGS="$LDFLAGS -lobjc -framework Cocoa"    
-		echo yes
-        return 0
-    done
-	
-	echo no
-	return 1
-}
+
+# start collecting dependency information
+echo > "$TEMP_DIR/flags.mk"
 
 DEPENDENCIES_FAILED=
 ERROR=0
-# general dependencies
-CFLAGS=
-LDFLAGS=
+# unnamed dependencies
+TEMP_CFLAGS=
+TEMP_CXXFLAGS=
+TEMP_LDFLAGS=
 while true
 do
-    if isnotplatform "macos"; then
+    while true
+    do
+        if [ -z "$lang_c" ] ; then
+            ERROR=1
+            break
+        fi
+
+        break
+    done
+    break
+done
+while true
+do
+    if notisplatform "macos"; then
         break
     fi
     while true
     do
-        
-		cat >> $TEMP_DIR/make.mk << __EOF__
-OBJ_EXT = .o
+
+        cat >> "$TEMP_DIR/make.mk" << __EOF__
+OBJ_EXT = o
 LIB_EXT = .a
 PACKAGE_SCRIPT = package_osx.sh
-
 __EOF__
-        
         break
     done
-    
     break
 done
 while true
 do
-    if isnotplatform "unix"; then
+    if notisplatform "unix"; then
         break
     fi
-    if isplatform "macos"; then
+    if isplatform "macos" || istoolchain "macos"; then
         break
     fi
     while true
     do
-        
-		cat >> $TEMP_DIR/make.mk << __EOF__
-OBJ_EXT = .o
+
+        cat >> "$TEMP_DIR/make.mk" << __EOF__
+OBJ_EXT = o
 LIB_EXT = .a
 PACKAGE_SCRIPT = package_unix.sh
-
 __EOF__
-        
         break
     done
-    
     break
 done
 while true
 do
-    if isnotplatform "bsd"; then
+    if notisplatform "bsd"; then
         break
     fi
     while true
     do
-        
-        CFLAGS="$CFLAGS -I/usr/local/include"    
-        LDFLAGS="$LDFLAGS -L/usr/local/lib"    
-        
+
+        TEMP_CFLAGS="$TEMP_CFLAGS -I/usr/local/include"
+        TEMP_LDFLAGS="$TEMP_LDFLAGS -L/usr/local/lib"
         break
     done
-    
     break
 done
 
-# add general dependency flags to config.mk
-echo >> $TEMP_DIR/config.mk
-if [ ! -z "${CFLAGS}" ]; then
-    echo "CFLAGS += $CFLAGS" >> $TEMP_DIR/config.mk
+# add general dependency flags to flags.mk
+echo "# general flags" >> "$TEMP_DIR/flags.mk"
+if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then
+    echo "CFLAGS += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
-if [ ! -z "${CXXFLAGS}" ]; then
-    echo "CXXFLAGS += $CXXFLAGS" >> $TEMP_DIR/config.mk
+if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then
+    echo "CXXFLAGS += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
-if [ ! -z "${LDFLAGS}" ]; then
-    echo "LDFLAGS += $LDFLAGS" >> $TEMP_DIR/config.mk
+if [ -n "${TEMP_LDFLAGS}" ]; then
+    echo "LDFLAGS += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
 
 #
@@ -545,274 +753,341 @@
 #
 checkopt_toolkit_gtk4()
 {
-	VERR=0
-	dependency_gtk4
-	if [ $? -ne 0 ]; then
-		VERR=1
-	fi
-	if [ $VERR -ne 0 ]; then
-		return 1
-	fi
-	cat >> $TEMP_DIR/make.mk << __EOF__
+    VERR=0
+    if dependency_error_gtk4 ; then
+        VERR=1
+    fi
+    if [ $VERR -ne 0 ]; then
+        return 1
+    fi
+    cat >> "$TEMP_DIR/make.mk" << __EOF__
 TOOLKIT = gtk
 GTKOBJ = draw_cairo.o
-
 __EOF__
-	return 0
+    return 0
 }
 checkopt_toolkit_gtk3()
 {
-	VERR=0
-	dependency_gtk3
-	if [ $? -ne 0 ]; then
-		VERR=1
-	fi
-	if [ $VERR -ne 0 ]; then
-		return 1
-	fi
-	cat >> $TEMP_DIR/make.mk << __EOF__
+    VERR=0
+    if dependency_error_gtk3 ; then
+        VERR=1
+    fi
+    if [ $VERR -ne 0 ]; then
+        return 1
+    fi
+    cat >> "$TEMP_DIR/make.mk" << __EOF__
 TOOLKIT = gtk
 GTKOBJ = draw_cairo.o
-
 __EOF__
-	return 0
+    return 0
 }
 checkopt_toolkit_gtk2()
 {
-	VERR=0
-	dependency_gtk2
-	if [ $? -ne 0 ]; then
-		VERR=1
-	fi
-	if [ $VERR -ne 0 ]; then
-		return 1
-	fi
-	cat >> $TEMP_DIR/make.mk << __EOF__
+    VERR=0
+    if dependency_error_gtk2 ; then
+        VERR=1
+    fi
+    if [ $VERR -ne 0 ]; then
+        return 1
+    fi
+    cat >> "$TEMP_DIR/make.mk" << __EOF__
 TOOLKIT = gtk
 GTKOBJ = draw_cairo.o
-
 __EOF__
-	return 0
+    return 0
 }
 checkopt_toolkit_gtk2legacy()
 {
-	VERR=0
-	dependency_gtk2legacy
-	if [ $? -ne 0 ]; then
-		VERR=1
-	fi
-	if [ $VERR -ne 0 ]; then
-		return 1
-	fi
-	cat >> $TEMP_DIR/make.mk << __EOF__
+    VERR=0
+    if dependency_error_gtk2legacy ; then
+        VERR=1
+    fi
+    if [ $VERR -ne 0 ]; then
+        return 1
+    fi
+    cat >> "$TEMP_DIR/make.mk" << __EOF__
 TOOLKIT = gtk
 GTKOBJ = draw_gdk.o
-
 __EOF__
-	return 0
+    return 0
 }
 checkopt_toolkit_qt5()
 {
-	VERR=0
-	dependency_qt5
-	if [ $? -ne 0 ]; then
-		VERR=1
-	fi
-	if [ $VERR -ne 0 ]; then
-		return 1
-	fi
-	cat >> $TEMP_DIR/make.mk << __EOF__
+    VERR=0
+    if dependency_error_qt5 ; then
+        VERR=1
+    fi
+    if [ $VERR -ne 0 ]; then
+        return 1
+    fi
+    cat >> "$TEMP_DIR/make.mk" << __EOF__
 TOOLKIT = qt
 LD = $(CXX)
-
 __EOF__
-	return 0
+    return 0
 }
 checkopt_toolkit_qt4()
 {
-	VERR=0
-	dependency_qt4
-	if [ $? -ne 0 ]; then
-		VERR=1
-	fi
-	if [ $VERR -ne 0 ]; then
-		return 1
-	fi
-	cat >> $TEMP_DIR/make.mk << __EOF__
+    VERR=0
+    if dependency_error_qt4 ; then
+        VERR=1
+    fi
+    if [ $VERR -ne 0 ]; then
+        return 1
+    fi
+    cat >> "$TEMP_DIR/make.mk" << __EOF__
 TOOLKIT = qt
 LD = $(CXX)
-
 __EOF__
-	return 0
+    return 0
 }
 checkopt_toolkit_motif()
 {
-	VERR=0
-	dependency_motif
-	if [ $? -ne 0 ]; then
-		VERR=1
-	fi
-	if [ $VERR -ne 0 ]; then
-		return 1
-	fi
-	cat >> $TEMP_DIR/make.mk << __EOF__
+    VERR=0
+    if dependency_error_motif ; then
+        VERR=1
+    fi
+    if [ $VERR -ne 0 ]; then
+        return 1
+    fi
+    cat >> "$TEMP_DIR/make.mk" << __EOF__
 TOOLKIT = motif
-
 __EOF__
-	return 0
+    return 0
 }
 
 #
 # TARGETS
 #
-CFLAGS=
-CXXFLAGS=
-LDFLAGS=
+
+echo >> "$TEMP_DIR/flags.mk"
+echo "configuring target: dav"
+echo "# flags for target dav" >> "$TEMP_DIR/flags.mk"
+TEMP_CFLAGS=
+TEMP_CXXFLAGS=
+TEMP_LDFLAGS=
+
+if dependency_error_curl; then
+    DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED curl "
+    ERROR=1
+fi
+if dependency_error_libxml2; then
+    DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED libxml2 "
+    ERROR=1
+fi
+if dependency_error_openssl; then
+    DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED openssl "
+    ERROR=1
+fi
+
+# Features
+
 
-# Target: tk
-CFLAGS=
-LDFLAGS=
-CXXFLAGS=
+if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then
+    echo "DAV_CFLAGS  += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk"
+fi
+if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then
+    echo "DAV_CXXFLAGS  += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk"
+fi
+if [ "$BUILD_TYPE" = "debug" ]; then
+    if [ -n "$lang_c" ]; then
+        echo 'DAV_CFLAGS += ${DEBUG_CC_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+    if [ -n "$lang_cpp" ]; then
+        echo 'DAV_CXXFLAGS += ${DEBUG_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+fi
+if [ "$BUILD_TYPE" = "release" ]; then
+    if [ -n "$lang_c" ]; then
+        echo 'DAV_CFLAGS += ${RELEASE_CC_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+    if [ -n "$lang_cpp" ]; then
+        echo 'DAV_CXXFLAGS += ${RELEASE_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+fi
+if [ -n "${TEMP_LDFLAGS}" ]; then
+    echo "DAV_LDFLAGS += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk"
+fi
+
+echo >> "$TEMP_DIR/flags.mk"
+echo "configuring target: tk"
+echo "# flags for target tk" >> "$TEMP_DIR/flags.mk"
+TEMP_CFLAGS=
+TEMP_CXXFLAGS=
+TEMP_LDFLAGS=
 
 
 # Features
 
 # Option: --toolkit
-if [ -z $OPT_TOOLKIT ]; then
-	SAVED_ERROR=$ERROR
-	SAVED_DEPENDENCIES_FAILED=$DEPENDENCIES_FAILED
-	ERROR=0
-	while true
-	do
-		if isplatform "windows"; then
-		checkopt_toolkit_winui
-		if [ $? -eq 0 ]; then
-			echo "  toolkit: winui" >> $TEMP_DIR/options
-			ERROR=0
-			break
-		fi
-		fi
-		if isplatform "macos"; then
-		checkopt_toolkit_cocoa
-		if [ $? -eq 0 ]; then
-			echo "  toolkit: cocoa" >> $TEMP_DIR/options
-			ERROR=0
-			break
-		fi
-		fi
-		checkopt_toolkit_gtk3
-		if [ $? -eq 0 ]; then
-			echo "  toolkit: gtk3" >> $TEMP_DIR/options
-			ERROR=0
-			break
-		fi
-		checkopt_toolkit_qt5
-		if [ $? -eq 0 ]; then
-			echo "  toolkit: qt5" >> $TEMP_DIR/options
-			ERROR=0
-			break
-		fi
-		checkopt_toolkit_gtk2
-		if [ $? -eq 0 ]; then
-			echo "  toolkit: gtk2" >> $TEMP_DIR/options
-			ERROR=0
-			break
-		fi
-		checkopt_toolkit_qt4
-		if [ $? -eq 0 ]; then
-			echo "  toolkit: qt4" >> $TEMP_DIR/options
-			ERROR=0
-			break
-		fi
-		checkopt_toolkit_motif
-		if [ $? -eq 0 ]; then
-			echo "  toolkit: motif" >> $TEMP_DIR/options
-			ERROR=0
-			break
-		fi
-		break
-	done
-	if [ $ERROR -ne 0 ]; then
-		SAVED_ERROR=1
-	fi
-	ERROR=$SAVED_ERROR
-	DEPENDENCIES_FAILED=$SAVED_DEPENDENCIES_FAILED=
+if [ -z "$OPT_TOOLKIT" ]; then
+    echo "auto-detecting option 'toolkit'"
+    SAVED_ERROR="$ERROR"
+    SAVED_DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED"
+    ERROR=1
+    while true
+    do
+        if isplatform "windows"; then
+        if checkopt_toolkit_winui ; then
+            echo "  toolkit: winui" >> "$TEMP_DIR/options"
+            ERROR=0
+            break
+        fi
+        fi
+        if isplatform "macos"; then
+        if checkopt_toolkit_cocoa ; then
+            echo "  toolkit: cocoa" >> "$TEMP_DIR/options"
+            ERROR=0
+            break
+        fi
+        fi
+        if checkopt_toolkit_gtk3 ; then
+            echo "  toolkit: gtk3" >> "$TEMP_DIR/options"
+            ERROR=0
+            break
+        fi
+        if checkopt_toolkit_qt5 ; then
+            echo "  toolkit: qt5" >> "$TEMP_DIR/options"
+            ERROR=0
+            break
+        fi
+        if checkopt_toolkit_gtk2 ; then
+            echo "  toolkit: gtk2" >> "$TEMP_DIR/options"
+            ERROR=0
+            break
+        fi
+        if checkopt_toolkit_qt4 ; then
+            echo "  toolkit: qt4" >> "$TEMP_DIR/options"
+            ERROR=0
+            break
+        fi
+        if checkopt_toolkit_motif ; then
+            echo "  toolkit: motif" >> "$TEMP_DIR/options"
+            ERROR=0
+            break
+        fi
+        break
+    done
+    if [ $ERROR -ne 0 ]; then
+        SAVED_ERROR=1
+        SAVED_DEPENDENCIES_FAILED="option 'toolkit' $SAVED_DEPENDENCIES_FAILED"
+    fi
+    ERROR="$SAVED_ERROR"
+    DEPENDENCIES_FAILED="$SAVED_DEPENDENCIES_FAILED"
 else
-	if false; then
-		false
-	elif [ $OPT_TOOLKIT = "gtk4" ]; then
-		echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
-		checkopt_toolkit_gtk4
-		if [ $? -ne 0 ]; then
-			ERROR=1
-		fi
-	elif [ $OPT_TOOLKIT = "gtk3" ]; then
-		echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
-		checkopt_toolkit_gtk3
-		if [ $? -ne 0 ]; then
-			ERROR=1
-		fi
-	elif [ $OPT_TOOLKIT = "gtk2" ]; then
-		echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
-		checkopt_toolkit_gtk2
-		if [ $? -ne 0 ]; then
-			ERROR=1
-		fi
-	elif [ $OPT_TOOLKIT = "gtk2legacy" ]; then
-		echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
-		checkopt_toolkit_gtk2legacy
-		if [ $? -ne 0 ]; then
-			ERROR=1
-		fi
-	elif [ $OPT_TOOLKIT = "qt5" ]; then
-		echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
-		checkopt_toolkit_qt5
-		if [ $? -ne 0 ]; then
-			ERROR=1
-		fi
-	elif [ $OPT_TOOLKIT = "qt4" ]; then
-		echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
-		checkopt_toolkit_qt4
-		if [ $? -ne 0 ]; then
-			ERROR=1
-		fi
-	elif [ $OPT_TOOLKIT = "motif" ]; then
-		echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
-		checkopt_toolkit_motif
-		if [ $? -ne 0 ]; then
-			ERROR=1
-		fi
-	fi
+    echo "checking option toolkit = $OPT_TOOLKIT"
+    if false; then
+        false
+    elif [ "$OPT_TOOLKIT" = "gtk4" ]; then
+        echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
+        if checkopt_toolkit_gtk4 ; then
+            :
+        else
+            ERROR=1
+            DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED"
+        fi
+    elif [ "$OPT_TOOLKIT" = "gtk3" ]; then
+        echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
+        if checkopt_toolkit_gtk3 ; then
+            :
+        else
+            ERROR=1
+            DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED"
+        fi
+    elif [ "$OPT_TOOLKIT" = "gtk2" ]; then
+        echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
+        if checkopt_toolkit_gtk2 ; then
+            :
+        else
+            ERROR=1
+            DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED"
+        fi
+    elif [ "$OPT_TOOLKIT" = "gtk2legacy" ]; then
+        echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
+        if checkopt_toolkit_gtk2legacy ; then
+            :
+        else
+            ERROR=1
+            DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED"
+        fi
+    elif [ "$OPT_TOOLKIT" = "qt5" ]; then
+        echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
+        if checkopt_toolkit_qt5 ; then
+            :
+        else
+            ERROR=1
+            DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED"
+        fi
+    elif [ "$OPT_TOOLKIT" = "qt4" ]; then
+        echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
+        if checkopt_toolkit_qt4 ; then
+            :
+        else
+            ERROR=1
+            DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED"
+        fi
+    elif [ "$OPT_TOOLKIT" = "motif" ]; then
+        echo "  toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options
+        if checkopt_toolkit_motif ; then
+            :
+        else
+            ERROR=1
+            DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED"
+        fi
+    fi
 fi
 
-echo >> $TEMP_DIR/config.mk
-if [ ! -z "${CFLAGS}" ]; then
-    echo "TK_CFLAGS  += $CFLAGS" >> $TEMP_DIR/config.mk
+if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then
+    echo "TK_CFLAGS  += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk"
+fi
+if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then
+    echo "TK_CXXFLAGS  += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
-if [ ! -z "${CXXFLAGS}" ]; then
-    echo "TK_CXXFLAGS += $CXXFLAGS" >> $TEMP_DIR/config.mk
+if [ "$BUILD_TYPE" = "debug" ]; then
+    if [ -n "$lang_c" ]; then
+        echo 'TK_CFLAGS += ${DEBUG_CC_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+    if [ -n "$lang_cpp" ]; then
+        echo 'TK_CXXFLAGS += ${DEBUG_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
 fi
-if [ ! -z "${LDFLAGS}" ]; then
-    echo "TK_LDFLAGS += $LDFLAGS" >> $TEMP_DIR/config.mk
+if [ "$BUILD_TYPE" = "release" ]; then
+    if [ -n "$lang_c" ]; then
+        echo 'TK_CFLAGS += ${RELEASE_CC_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+    if [ -n "$lang_cpp" ]; then
+        echo 'TK_CXXFLAGS += ${RELEASE_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+fi
+if [ -n "${TEMP_LDFLAGS}" ]; then
+    echo "TK_LDFLAGS += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
 
+
+# final result
 if [ $ERROR -ne 0 ]; then
-	echo
-	echo "Error: Unresolved dependencies"
-	echo $DEPENDENCIES_FAILED
-	rm -Rf $TEMP_DIR
-	exit 1
+    echo
+    echo "Error: Unresolved dependencies"
+    echo "$DEPENDENCIES_FAILED"
+    abort_configure
 fi
 
 echo "configure finished"
 echo
 echo "Build Config:"
-echo "  PREFIX:    $PREFIX"
-echo "  TOOLCHAIN: $TOOLCHAIN_NAME"
+echo "  PREFIX:      $prefix"
+echo "  TOOLCHAIN:   $TOOLCHAIN_NAME"
 echo "Options:"
-cat $TEMP_DIR/options
+cat "$TEMP_DIR/options"
 echo
-cat $TEMP_DIR/config.mk $TEMP_DIR/make.mk > config.mk
-rm -Rf $TEMP_DIR
 
+# generate the config.mk file
+cat > "$TEMP_DIR/config.mk" << __EOF__
+#
+# config.mk generated by configure
+#
 
+__EOF__
+write_toolchain_defaults "$TEMP_DIR/toolchain.mk"
+cat "$TEMP_DIR/vars.mk" "$TEMP_DIR/toolchain.mk" "$TEMP_DIR/flags.mk" "$TEMP_DIR/make.mk" > config.mk
+rm -Rf "$TEMP_DIR"
--- a/make/Makefile.mk	Mon Feb 12 21:13:23 2024 +0100
+++ b/make/Makefile.mk	Sun Jun 09 15:43:08 2024 +0200
@@ -32,11 +32,11 @@
 include config.mk
 
 BUILD_DIRS = build/bin build/lib
-BUILD_DIRS += build/application build/ucx
+BUILD_DIRS += build/application build/ucx build/libidav
 BUILD_DIRS += build/ui/common build/ui/$(TOOLKIT)
 
-all: $(BUILD_DIRS) ucx ui application
-	make/$(PACKAGE_SCRIPT)
+all: $(BUILD_DIRS) ucx ui libidav application
+	
 
 $(BUILD_DIRS):
 	mkdir -p $@
@@ -47,7 +47,10 @@
 ucx: FORCE
 	cd ucx; $(MAKE) all
 
-application: ui FORCE
+libidav: ucx FORCE
+	cd libidav; $(MAKE) all
+
+application: ui libidav FORCE
 	cd application; $(MAKE)
 
 FORCE:
--- a/make/configure.vm	Mon Feb 12 21:13:23 2024 +0100
+++ b/make/configure.vm	Sun Jun 09 15:43:08 2024 +0200
@@ -1,78 +1,65 @@
 #!/bin/sh
 
+# create temporary directory
+TEMP_DIR=".tmp-`uname -n`"
+rm -Rf "$TEMP_DIR"
+if mkdir -p "$TEMP_DIR"; then
+    :
+else
+    echo "Cannot create tmp dir $TEMP_DIR"
+    echo "Abort"
+    exit 1
+fi
+touch "$TEMP_DIR/options"
+touch "$TEMP_DIR/features"
+
+# define standard variables
+# also define standard prefix (this is where we will search for config.site)
+prefix=/usr
+exec_prefix=
+bindir=
+sbindir=
+libdir=
+libexecdir=
+datarootdir=
+datadir=
+sysconfdir=
+sharedstatedir=
+localstatedir=
+runstatedir=
+includedir=
+infodir=
+localedir=
+mandir=
+
+# custom variables
 #foreach( $var in $vars )
 #if( $var.exec )
-${var.name}=`${var.value}`
+${var.varName}=`${var.value}`
 #else
-${var.name}=${var.value}
+${var.varName}="${var.value}"
 #end
 #end
 
-#if ( ! $project.hasVar("PREFIX") )
-PREFIX=/usr
+# features
+#foreach( $feature in $features )
+#if( ${feature.auto} )
+${feature.varName}=auto
 #end
-#if ( ! $project.hasVar("EPREFIX") )
-EPREFIX=$PREFIX
 #end
 
-#if ( ! $project.hasVar("BINDIR") )
-BINDIR=
-#end
-#if ( ! $project.hasVar("SBINDIR") )
-SBINDIR=
-#end
-#if ( ! $project.hasVar("LIBDIR") )
-LIBDIR=
-#end
-#if ( ! $project.hasVar("LIBEXECDIR") )
-LIBEXECDIR=
-#end
-#if ( ! $project.hasVar("DATADIR") )
-DATADIR=
-#end
-#if ( ! $project.hasVar("SYSCONFDIR") )
-SYSCONFDIR=
-#end
-#if ( ! $project.hasVar("SHAREDSTATEDIR") )
-SHAREDSTATEDIR=
-#end
-#if ( ! $project.hasVar("LOCALSTATEDIR") )
-LOCALSTATEDIR=
-#end
-#if ( ! $project.hasVar("INCLUDEDIR") )
-INCLUDEDIR=
-#end
-#if ( ! $project.hasVar("INFODIR") )
-INFODIR=
-#end
-#if ( ! $project.hasVar("MANDIR") )
-MANDIR=
-#end
-
-OS=`uname -s`
-OS_VERSION=`uname -r`
-
-TEMP_DIR=".tmp-`uname -n`"
-mkdir -p $TEMP_DIR
-if [ $? -ne 0 ]; then
-	echo "Cannot create tmp dir"
-	echo "Abort"
-fi
-touch $TEMP_DIR/options
-touch $TEMP_DIR/features
-
-# features
-#foreach( $feature in $features )
-#if( ${feature.isDefault()} )
-${feature.getVarName()}=on
-#end
-#end
+# clean abort
+abort_configure()
+{
+    rm -Rf "$TEMP_DIR"
+    exit 1
+}
 
 # help text
 printhelp()
 {
-	echo "Usage: $0 [OPTIONS]..."
-	cat << __EOF__
+    echo "Usage: $0 [OPTIONS]..."
+    cat << __EOF__
 Installation directories:
   --prefix=PREFIX         path prefix for architecture-independent files
                           [/usr]
@@ -85,28 +72,28 @@
   --sysconfdir=DIR        system configuration files [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       run-time variable data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
   --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
   --infodir=DIR           info documentation [DATAROOTDIR/info]
   --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
 
 #if( $options.size() > 0 )
 Options:
+  --debug                 add extra compile flags for debug builds
+  --release               add extra compile flags for release builds
 #foreach( $opt in $options )
-  --${opt.getArgument()}=${opt.getValuesString()}
+  --${opt.argument}=${opt.valuesString}
 #end
 
 #end
 #if( $features.size() > 0 )
 Optional Features:
 #foreach( $feature in $features )
-#if( $feature.default )
-  --disable-${feature.arg}
-#else
-  --enable-${feature.arg}
-#end
+${feature.helpText}
 #end
 
 #end
@@ -114,297 +101,328 @@
 }
 
 #
-# parse arguments 
+# parse arguments
 #
+BUILD_TYPE="default"
 #set( $D = '$' )
-for ARG in $@
+for ARG in "$@"
 do
     case "$ARG" in
-		"--prefix="*)         PREFIX=${D}{ARG#--prefix=} ;;
-		"--exec-prefix="*)    EPREFIX=${D}{ARG#--exec-prefix=} ;;
-		"--bindir="*)         BINDIR=${D}{ARG#----bindir=} ;;
-		"--sbindir="*)        SBINDIR=${D}{ARG#--sbindir=} ;;
-		"--libdir="*)         LIBDIR=${D}{ARG#--libdir=} ;;
-		"--libexecdir="*)     LIBEXECDIR=${D}{ARG#--libexecdir=} ;;
-		"--datadir="*)        DATADIR=${D}{ARG#--datadir=} ;;
-		"--sysconfdir="*)     SYSCONFDIR=${D}{ARG#--sysconfdir=} ;;
-		"--sharedstatedir="*) SHAREDSTATEDIR=${D}{ARG#--sharedstatedir=} ;;
-		"--localstatedir="*)  LOCALSTATEDIR=${D}{ARG#--localstatedir=} ;;
-		"--includedir="*)     INCLUDEDIR=${D}{ARG#--includedir=} ;;
-		"--infodir="*)        INFODIR=${D}{ARG#--infodir=} ;;
-		"--mandir"*)          MANDIR=${D}{ARG#--mandir} ;;
-		"--help"*) printhelp; exit 1 ;;
-	#foreach( $opt in $options )
-    	"--${opt.getArgument()}="*) ${opt.getVarName()}=${D}{ARG#--${opt.getArgument()}=} ;;
+        "--prefix="*)         prefix=${D}{ARG#--prefix=} ;;
+        "--exec-prefix="*)    exec_prefix=${D}{ARG#--exec-prefix=} ;;
+        "--bindir="*)         bindir=${D}{ARG#----bindir=} ;;
+        "--sbindir="*)        sbindir=${D}{ARG#--sbindir=} ;;
+        "--libdir="*)         libdir=${D}{ARG#--libdir=} ;;
+        "--libexecdir="*)     libexecdir=${D}{ARG#--libexecdir=} ;;
+        "--datarootdir="*)    datarootdir=${D}{ARG#--datarootdir=} ;;
+        "--datadir="*)        datadir=${D}{ARG#--datadir=} ;;
+        "--sysconfdir="*)     sysconfdir=${D}{ARG#--sysconfdir=} ;;
+        "--sharedstatedir="*) sharedstatedir=${D}{ARG#--sharedstatedir=} ;;
+        "--localstatedir="*)  localstatedir=${D}{ARG#--localstatedir=} ;;
+        "--includedir="*)     includedir=${D}{ARG#--includedir=} ;;
+        "--infodir="*)        infodir=${D}{ARG#--infodir=} ;;
+        "--mandir"*)          mandir=${D}{ARG#--mandir} ;;
+        "--localedir"*)       localedir=${D}{ARG#--localedir} ;;
+        "--help"*) printhelp; abort_configure ;;
+        "--debug")           BUILD_TYPE="debug" ;;
+        "--release")         BUILD_TYPE="release" ;;
+    #foreach( $opt in $options )
+        "--${opt.argument}="*) ${opt.varName}=${D}{ARG#--${opt.argument}=} ;;
     #end
-	#foreach( $feature in $features )
-		"--enable-${feature.arg}") ${feature.getVarName()}=on ;;
-		"--disable-${feature.arg}") unset ${feature.getVarName()} ;;
-	#end
-		"-"*) echo "unknown option: $ARG"; exit 1 ;;
-	esac
+    #foreach( $feature in $features )
+        "--enable-${feature.arg}") ${feature.varName}=on ;;
+        "--disable-${feature.arg}") unset ${feature.varName} ;;
+    #end
+        "-"*) echo "unknown option: $ARG"; abort_configure ;;
+    esac
 done
 
-# set dir variables
-if [ -z "$BINDIR" ]; then
-	BINDIR=$EPREFIX/bin
-fi
-if [ -z "$SBINDIR" ]; then
-	SBINDIR=$EPREFIX/sbin
-fi
-if [ -z "$LIBDIR" ]; then
-	LIBDIR=$EPREFIX/lib
-fi
-if [ -z "$LIBEXEC" ]; then
-	LIBEXECDIR=$EPREFIX/libexec
-fi
-if [ -z "$DATADIR" ]; then
-	DATADIR=$PREFIX/share
-fi
-if [ -z "$SYSCONFDIR" ]; then
-	SYSCONFDIR=$PREFIX/etc
-fi
-if [ -z "$SHAREDSTATEDIR" ]; then
-	SHAREDSTATEDIR=$PREFIX/com
-fi
-if [ -z "$LOCALSTATEDIR" ]; then
-	LOCALSTATEDIR=$PREFIX/var
-fi
-if [ -z "$INCLUDEDIR" ]; then
-	INCLUDEDIR=$PREFIX/include
-fi
-if [ -z "$INFODIR" ]; then
-	INFODIR=$PREFIX/info
-fi
-if [ -z "$MANDIR" ]; then
-	MANDIR=$PREFIX/man
+## Begin unparsed content. **
+#[[
+
+# set defaults for dir variables
+: ${exec_prefix:="$prefix"}
+: ${bindir:='${exec_prefix}/bin'}
+: ${sbindir:='${exec_prefix}/sbin'}
+: ${libdir:='${exec_prefix}/lib'}
+: ${libexecdir:='${exec_prefix}/libexec'}
+: ${datarootdir:='${prefix}/share'}
+: ${datadir:='${datarootdir}'}
+: ${sysconfdir:='${prefix}/etc'}
+: ${sharedstatedir:='${prefix}/com'}
+: ${localstatedir:='${prefix}/var'}
+: ${runstatedir:='${localstatedir}/run'}
+: ${includedir:='${prefix}/include'}
+: ${infodir:='${datarootdir}/info'}
+: ${mandir:='${datarootdir}/man'}
+: ${localedir:='${datarootdir}/locale'}
+
+# check if a config.site exists and load it
+if [ -n "$CONFIG_SITE" ]; then
+    # CONFIG_SITE may contain space separated file names
+    for cs in $CONFIG_SITE; do
+        printf "loading defaults from $cs... "
+        . "$cs"
+        echo ok
+    done
+elif [ -f "$prefix/share/config.site" ]; then
+    printf "loading site defaults... "
+    . "$prefix/share/config.site"
+    echo ok
+elif [ -f "$prefix/etc/config.site" ]; then
+    printf "loading site defaults... "
+    . "$prefix/etc/config.site"
+    echo ok
 fi
 
-which pkg-config > /dev/null
-if [ $? -eq 0 ]; then
-    PKG_CONFIG=pkg-config
-else
-    PKG_CONFIG=false
-fi
+# Test for availability of pkg-config
+PKG_CONFIG=`command -v pkg-config`
+: ${PKG_CONFIG:="false"}
 
 # Simple uname based platform detection
 # $PLATFORM is used for platform dependent dependency selection
+OS=`uname -s`
+OS_VERSION=`uname -r`
 printf "detect platform... "
-if [ $OS = SunOS ]; then
+if [ "$OS" = "SunOS" ]; then
     PLATFORM="solaris sunos unix svr4"
-fi
-if [ $OS = Linux ]; then
+elif [ "$OS" = "Linux" ]; then
     PLATFORM="linux unix"
-fi
-if [ $OS = FreeBSD ]; then
+elif [ "$OS" = "FreeBSD" ]; then
     PLATFORM="freebsd bsd unix"
-fi
-if [ $OS = Darwin ]; then
+elif [ "$OS" = "OpenBSD" ]; then
+    PLATFORM="openbsd bsd unix"
+elif [ "$OS" = "NetBSD" ]; then
+    PLATFORM="netbsd bsd unix"
+elif [ "$OS" = "Darwin" ]; then
     PLATFORM="macos osx bsd unix"
-fi
-echo $OS | grep "MINGW" > /dev/null
-if [ $? -eq 0 ]; then
+elif echo "$OS" | grep -i "MINGW" > /dev/null; then
     PLATFORM="windows mingw"
 fi
-
-if [ -z "$PLATFORM" ]; then
-    PLATFORM="unix"
-fi
+: ${PLATFORM:="unix"}
 
-for p in $PLATFORM
-do
-	PLATFORM_NAME=$p
-	break
-done
-echo $PLATFORM_NAME
+PLATFORM_NAME=`echo "$PLATFORM" | cut -f1 -d' ' -`
+echo "$PLATFORM_NAME"
 
 isplatform()
 {
     for p in $PLATFORM
     do
-        if [ $p = $1 ]; then
+        if [ "$p" = "$1" ]; then
             return 0
         fi
     done
     return 1
 }
-isnotplatform()
+notisplatform()
 {
     for p in $PLATFORM
     do
-        if [ $p = $1 ]; then
+        if [ "$p" = "$1" ]; then
+            return 1
+        fi
+    done
+    return 0
+}
+istoolchain()
+{
+    for t in $TOOLCHAIN
+    do
+        if [ "$t" = "$1" ]; then
+            return 0
+        fi
+    done
+    return 1
+}
+notistoolchain()
+{
+    for t in $TOOLCHAIN
+    do
+        if [ "$t" = "$1" ]; then
             return 1
         fi
     done
     return 0
 }
-
-# generate config.mk and config.h
-cat > $TEMP_DIR/config.mk << __EOF__
-#
-# config.mk generated by configure
-#
-
-# general vars
-#foreach( $var in $vars )
-${var.name}=$${var.name}
-#end
+]]#
+## End of unparsed content **
 
-#if ( ! $project.hasVar("PREFIX") )
-PREFIX=$PREFIX
-#end
-#if ( ! $project.hasVar("EPREFIX") )
-EPREFIX=$EPREFIX
-#end
-
-#if ( ! $project.hasVar("BINDIR") )
-BINDIR=$BINDIR
-#end
-#if ( ! $project.hasVar("SBINDIR") )
-SBINDIR=$SBINDIR
+# generate vars.mk
+cat > "$TEMP_DIR/vars.mk" << __EOF__
+prefix=$prefix
+exec_prefix=$exec_prefix
+bindir=$bindir
+sbindir=$sbindir
+libdir=$libdir
+libexecdir=$libexecdir
+datarootdir=$datarootdir
+datadir=$datadir
+sysconfdir=$sysconfdir
+sharedstatedir=$sharedstatedir
+localstatedir=$localstatedir
+runstatedir=$runstatedir
+includedir=$includedir
+infodir=$infodir
+mandir=$mandir
+localedir=$localedir
+#foreach( $var in $vars )
+${var.varName}=${D}${var.varName}
 #end
-#if ( ! $project.hasVar("LIBDIR") )
-LIBDIR=$LIBDIR
-#end
-#if ( ! $project.hasVar("LIBEXECDIR") )
-LIBEXECDIR=$LIBEXECDIR
-#end
-#if ( ! $project.hasVar("DATADIR") )
-DATADIR=$DATADIR
-#end
-#if ( ! $project.hasVar("SYSCONFDIR") )
-SYSCONFDIR=$SYSCONFDIR
-#end
-#if ( ! $project.hasVar("SHAREDSTATEDIR") )
-SHAREDSTATEDIR=$SHAREDSTATEDIR
-#end
-#if ( ! $project.hasVar("LOCALSTATEDIR") )
-LOCALSTATEDIR=$LOCALSTATEDIR
-#end
-#if ( ! $project.hasVar("INCLUDEDIR") )
-INCLUDEDIR=$INCLUDEDIR
-#end
-#if ( ! $project.hasVar("INFODIR") )
-INFODIR=$INFODIR
-#end
-#if ( ! $project.hasVar("MANDIR") )
-MANDIR=$MANDIR
-#end
-
 __EOF__
 
-echo > $TEMP_DIR/make.mk
-
-ENV_CFLAGS=$CFLAGS
-ENV_LDFLAGS=$LDFLAGS
-ENV_CXXFLAGS=$CXXFLAGS
-
-# Toolchain detection
-# this will insert make vars to config.mk
+# toolchain detection utilities
 . make/toolchain.sh
 
-# add user specified flags to config.mk
-echo >> $TEMP_DIR/config.mk
-if [ ! -z "${ENV_CFLAGS}" ]; then
-    echo "CFLAGS += $ENV_CFLAGS" >> $TEMP_DIR/config.mk
-fi
-if [ ! -z "${ENV_CXXFLAGS}" ]; then
-    echo "CXXFLAGS += $ENV_CXXFLAGS" >> $TEMP_DIR/config.mk
-fi
-if [ ! -z "${ENV_LDFLAGS}" ]; then
-    echo "LDFLAGS += $ENV_LDFLAGS" >> $TEMP_DIR/config.mk
-fi
-
 #
 # DEPENDENCIES
 #
 
-#foreach( $dependency in $namedDependencies )
-dependency_${dependency.name}()
+# check languages
+lang_c=
+lang_cpp=
+#foreach( $lang in $languages )
+if detect_${lang}_compiler ; then
+    lang_${lang}=1
+fi
+#end
+
+# create buffer for make variables required by dependencies
+echo > "$TEMP_DIR/make.mk"
+
+test_pkg_config()
 {
-    printf "checking for ${dependency.name}... "
-    #foreach( $sub in $dependency.getSubdependencies() )
-    # dependency $sub.name $sub.getPlatformString()
+    if "$PKG_CONFIG" --exists "$1" ; then :
+    else return 1 ; fi
+    if [ -z "$2" ] || "$PKG_CONFIG" --atleast-version="$2" "$1" ; then :
+    else return 1 ; fi
+    if [ -z "$3" ] || "$PKG_CONFIG" --exact-version="$3" "$1" ; then :
+    else return 1 ; fi
+    if [ -z "$4" ] || "$PKG_CONFIG" --max-version="$4" "$1" ; then :
+    else return 1 ; fi
+    return 0
+}
+
+print_check_msg()
+{
+    if [ -z "$1" ]; then
+        shift
+        printf "$@"
+    fi
+}
+
+#foreach( $dependency in $namedDependencies )
+dependency_error_${dependency.id}()
+{
+    print_check_msg "${D}dep_checked_${dependency.id}" "checking for ${dependency.name}... "
+    #foreach( $sub in $dependency.subdependencies )
+    # dependency $sub.fullName
     while true
     do
-    	#if( $sub.platform )
-    	if isnotplatform "${sub.platform}"; then
+        #if( $sub.platform )
+        if notisplatform "${sub.platform}"; then
+            break
+        fi
+        #end
+        #if( $sub.toolchain )
+        if notistoolchain "${sub.toolchain}"; then
+            break
+        fi
+        #end
+        #foreach( $np in $sub.notList )
+        if isplatform "${np}" || istoolchain "${np}"; then
             break
         fi
-    	#end
-		#foreach( $not in $sub.getNotList() )
-		if isplatform "${not}"; then
+        #end
+        #foreach( $lang in $sub.lang )
+        if [ -z "$lang_${lang}" ] ; then
             break
         fi
-		#end
+        #end
         #if( $sub.pkgconfig.size() > 0 )
         if [ -z "$PKG_CONFIG" ]; then
-        	break
+            break
+        fi
+        #end
+        #foreach( $test in $sub.tests )
+        if $test > /dev/null ; then
+            :
+        else
+            break
         fi
         #end
         #foreach( $pkg in $sub.pkgconfig )
-		$PKG_CONFIG $pkg.getPkgConfigParam()
-        if [ $? -ne 0 ] ; then
+        if test_pkg_config "$pkg.name" "$pkg.atleast" "$pkg.exact" "$pkg.max" ; then
+            TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags $pkg.name`"
+            TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs $pkg.name`"
+        else
             break
         fi
-        CFLAGS="$CFLAGS `$PKG_CONFIG --cflags $pkg.getPkgConfigParam()`"
-        LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs $pkg.getPkgConfigParam()`"
         #end
         #foreach( $flags in $sub.flags )
         #if( $flags.exec )
-        $flags.value > /dev/null
-        if [ $? -eq 0 ]; then
-            $flags.varName="$$flags.varName `$flags.value`"
+        if tmp_flags=`$flags.value` ; then
+            TEMP_$flags.varName="$TEMP_$flags.varName $tmp_flags"
         else
             break
         fi
         #else
-        $flags.varName="$$flags.varName $flags.value"    
+        TEMP_$flags.varName="$TEMP_$flags.varName $flags.value"
         #end
         #end
-        #foreach( $test in $sub.tests )
-        $test > /dev/null
-        if [ $? -ne 0 ]; then
-        	break
-        fi
-        #end
-		#if ( $sub.make.length() > 0 )
-		cat >> $TEMP_DIR/make.mk << __EOF__
-# Dependency: $dependency.name		
+        #if ( $sub.make.length() > 0 )
+        cat >> $TEMP_DIR/make.mk << __EOF__
+# Dependency: $dependency.name
 $sub.make
 __EOF__
         #end
-		echo yes
-        return 0
+        print_check_msg "${D}dep_checked_${dependency.id}" "yes\n"
+        dep_checked_${dependency.id}=1
+        return 1
     done
-	
-	#end
-	echo no
-	return 1
+
+    #end
+    print_check_msg "${D}dep_checked_${dependency.id}" "no\n"
+    dep_checked_${dependency.id}=1
+    return 0
 }
 #end
 
+# start collecting dependency information
+echo > "$TEMP_DIR/flags.mk"
+
 DEPENDENCIES_FAILED=
 ERROR=0
 #if( $dependencies.size() > 0 )
-# general dependencies
-CFLAGS=
-LDFLAGS=
+# unnamed dependencies
+TEMP_CFLAGS=
+TEMP_CXXFLAGS=
+TEMP_LDFLAGS=
 #foreach( $dependency in $dependencies )
 while true
 do
-	#if( $dependency.platform )
-    if isnotplatform "${dependency.platform}"; then
+    #if( $dependency.platform )
+    if notisplatform "${dependency.platform}"; then
+        break
+    fi
+    #end
+    #if( $dependency.toolchain )
+    if notistoolchain "${dependency.toolchain}"; then
         break
     fi
     #end
-	#foreach( $not in $dependency.getNotList() )
-    if isplatform "${not}"; then
+    #foreach( $np in $dependency.notList )
+    if isplatform "${np}" || istoolchain "${np}"; then
         break
     fi
-	#end
+    #end
     while true
     do
+        #foreach( $lang in $dependency.lang )
+        if [ -z "$lang_${lang}" ] ; then
+            ERROR=1
+            break
+        fi
+        #end
         #if( $dependency.pkgconfig.size() > 0 )
         if [ -z "$PKG_CONFIG" ]; then
             ERROR=1
@@ -412,54 +430,54 @@
         fi
         #end
         #foreach( $pkg in $dependency.pkgconfig )
-        printf "checking for pkg-config package $pkg.getPkgConfigParam()... "
-		$PKG_CONFIG $pkg.getPkgConfigParam()
-        if [ $? -ne 0 ]; then
-            echo no
+        print_check_msg "${D}dep_pkgconfig_checked_${pkg.id}" "checking for pkg-config package $pkg.name... "
+        if test_pkg_config "$pkg.name" "$pkg.atleast" "$pkg.exact" "$pkg.max" ; then
+            print_check_msg "${D}dep_pkgconfig_checked_${pkg.id}" "yes\n"
+            dep_pkgconfig_checked_${pkg.id}=1
+            TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags $pkg.name`"
+            TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs $pkg.name`"
+        else
+            print_check_msg "${D}dep_pkgconfig_checked_${pkg.id}" "no\n"
+            dep_pkgconfig_checked_${pkg.id}=1
             ERROR=1
             break
         fi
-        echo yes
-        CFLAGS="$CFLAGS `$PKG_CONFIG --cflags $pkg.getPkgConfigParam()`"
-        LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs $pkg.getPkgConfigParam()`"
         #end
-        
+
         #foreach( $flags in $dependency.flags )
         #if( $flags.exec )
         $flags.value > /dev/null
-        if [ $? -ne 0 ]; then
-            $flags.varName="$$flags.varName `$flags.value`"
+        if tmp_flags=`$flags.value` ; then
+            TEMP_$flags.varName="$TEMP_$flags.varName $tmp_flags"
         else
             ERROR=1
             break
         fi
         #else
-        $flags.varName="$$flags.varName $flags.value"    
+        TEMP_$flags.varName="$TEMP_$flags.varName $flags.value"
         #end
         #end
-		#if ( $dependency.make.length() > 0 )
-		cat >> $TEMP_DIR/make.mk << __EOF__
+        #if ( $dependency.make.length() > 0 )
+        cat >> "$TEMP_DIR/make.mk" << __EOF__
 $dependency.make
 __EOF__
         #end
-        
         break
     done
-    
     break
 done
 #end
 
-# add general dependency flags to config.mk
-echo >> $TEMP_DIR/config.mk
-if [ ! -z "${CFLAGS}" ]; then
-    echo "CFLAGS += $CFLAGS" >> $TEMP_DIR/config.mk
+# add general dependency flags to flags.mk
+echo "# general flags" >> "$TEMP_DIR/flags.mk"
+if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then
+    echo "CFLAGS += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
-if [ ! -z "${CXXFLAGS}" ]; then
-    echo "CXXFLAGS += $CXXFLAGS" >> $TEMP_DIR/config.mk
+if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then
+    echo "CXXFLAGS += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
-if [ ! -z "${LDFLAGS}" ]; then
-    echo "LDFLAGS += $LDFLAGS" >> $TEMP_DIR/config.mk
+if [ -n "${TEMP_LDFLAGS}" ]; then
+    echo "LDFLAGS += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
 #end
 
@@ -470,25 +488,25 @@
 #foreach( $val in $opt.values )
 ${val.func}()
 {
-	VERR=0
-	#foreach( $dep in $val.dependencies )
-	dependency_$dep
-	if [ $? -ne 0 ]; then
-		VERR=1
-	fi
-	#end
-	if [ $VERR -ne 0 ]; then
-		return 1
-	fi
-	#foreach( $def in $val.defines )
-		CFLAGS="$CFLAGS ${def.toFlags()}"
-	#end
-	#if( $val.hasMake() )
-	cat >> $TEMP_DIR/make.mk << __EOF__
+    VERR=0
+    #foreach( $dep in $val.dependencies )
+    if dependency_error_$dep ; then
+        VERR=1
+    fi
+    #end
+    if [ $VERR -ne 0 ]; then
+        return 1
+    fi
+    #foreach( $def in $val.defines )
+        TEMP_CFLAGS="$TEMP_CFLAGS ${def.toFlags()}"
+        TEMP_CXXFLAGS="$TEMP_CXXFLAGS ${def.toFlags()}"
+    #end
+    #if( $val.hasMake() )
+    cat >> "$TEMP_DIR/make.mk" << __EOF__
 $val.make
 __EOF__
-	#end
-	return 0
+    #end
+    return 0
 }
 #end
 #end
@@ -496,120 +514,160 @@
 #
 # TARGETS
 #
-CFLAGS=
-CXXFLAGS=
-LDFLAGS=
 
 #foreach( $target in $targets )
+echo >> "$TEMP_DIR/flags.mk"
 #if ( $target.name )
-# Target: $target.name
+echo "configuring target: $target.name"
+echo "# flags for target $target.name" >> "$TEMP_DIR/flags.mk"
 #else
-# Target
+echo "configuring global target"
+echo "# flags for unnamed target" >> "$TEMP_DIR/flags.mk"
 #end
-CFLAGS=
-LDFLAGS=
-CXXFLAGS=
+TEMP_CFLAGS=
+TEMP_CXXFLAGS=
+TEMP_LDFLAGS=
 
 #foreach( $dependency in $target.dependencies )
-dependency_$dependency
-if [ $? -ne 0 ]; then
-	DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} "
-	ERROR=1
+if dependency_error_$dependency; then
+    DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} "
+    ERROR=1
 fi
 #end
 
 # Features
 #foreach( $feature in $target.features )
-if [ ! -z "$${feature.getVarName()}" ]; then
+if [ -n "${D}${feature.varName}" ]; then
 #foreach( $dependency in $feature.dependencies )
-	# check dependency
-	dependency_$dependency
-	if [ $? -ne 0 ]; then
-		# "auto" features can fail and are just disabled in this case
-		if [ $${feature.getVarName()} != "auto" ]; then
-			DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} "
-			ERROR=1
-		fi
-	fi
+    # check dependency
+    if dependency_error_$dependency ; then
+        # "auto" features can fail and are just disabled in this case
+        if [ "${D}${feature.varName}" = "auto" ]; then
+            DISABLE_${feature.varName}=1
+        else
+            DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} "
+            ERROR=1
+        fi
+    fi
 #end
+    if [ -n "$DISABLE_${feature.varName}" ]; then
+        unset ${feature.varName}
+    fi
 fi
 #end
 
 #foreach( $opt in $target.options )
 # Option: --${opt.argument}
-if [ -z ${D}${opt.getVarName()} ]; then
-	SAVED_ERROR=$ERROR
-	SAVED_DEPENDENCIES_FAILED=$DEPENDENCIES_FAILED
-	ERROR=0
-	while true
-	do
-		#foreach( $optdef in $opt.defaults )
-		#if( $optdef.platform )
-		if isplatform "$optdef.platform"; then
-		#end
-		$optdef.func
-		if [ $? -eq 0 ]; then
-			echo "  ${opt.argument}: ${optdef.valueName}" >> $TEMP_DIR/options
-			ERROR=0
-			break
-		fi
-		#if( $optdef.platform )
-		fi
-		#end
-		#end
-		break
-	done
-	if [ $ERROR -ne 0 ]; then
-		SAVED_ERROR=1
-	fi
-	ERROR=$SAVED_ERROR
-	DEPENDENCIES_FAILED=$SAVED_DEPENDENCIES_FAILED=
+if [ -z "${D}${opt.varName}" ]; then
+    echo "auto-detecting option '${opt.argument}'"
+    SAVED_ERROR="$ERROR"
+    SAVED_DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED"
+    ERROR=1
+    while true
+    do
+        #foreach( $optdef in $opt.defaults )
+        #if( $optdef.platform )
+        if isplatform "$optdef.platform"; then
+        #end
+        if $optdef.func ; then
+            echo "  ${opt.argument}: ${optdef.valueName}" >> "$TEMP_DIR/options"
+            ERROR=0
+            break
+        fi
+        #if( $optdef.platform )
+        fi
+        #end
+        #end
+        break
+    done
+    if [ $ERROR -ne 0 ]; then
+        SAVED_ERROR=1
+        SAVED_DEPENDENCIES_FAILED="option '${opt.argument}' $SAVED_DEPENDENCIES_FAILED"
+    fi
+    ERROR="$SAVED_ERROR"
+    DEPENDENCIES_FAILED="$SAVED_DEPENDENCIES_FAILED"
 else
-	if false; then
-		false
-	#foreach( $optval in $opt.values )
-	elif [ ${D}${opt.getVarName()} = "${optval.value}" ]; then
-		echo "  ${opt.argument}: ${D}${opt.getVarName()}" >> $TEMP_DIR/options
-		$optval.func
-		if [ $? -ne 0 ]; then
-			ERROR=1
-		fi
-	#end
-	fi
+    echo "checking option ${opt.argument} = ${D}${opt.varName}"
+    if false; then
+        false
+    #foreach( $optval in $opt.values )
+    elif [ "${D}${opt.varName}" = "${optval.value}" ]; then
+        echo "  ${opt.argument}: ${D}${opt.varName}" >> $TEMP_DIR/options
+        if $optval.func ; then
+            :
+        else
+            ERROR=1
+            DEPENDENCIES_FAILED="option '${opt.argument}' $DEPENDENCIES_FAILED"
+        fi
+    #end
+    fi
 fi
 #end
 
-echo >> $TEMP_DIR/config.mk
-if [ ! -z "${CFLAGS}" ]; then
-    echo "${target.getCFlags()}  += $CFLAGS" >> $TEMP_DIR/config.mk
+if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then
+    echo "${target.cFlags}  += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk"
+fi
+if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then
+    echo "${target.cxxFlags}  += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
-if [ ! -z "${CXXFLAGS}" ]; then
-    echo "${target.getCXXFlags()} += $CXXFLAGS" >> $TEMP_DIR/config.mk
+if [ "$BUILD_TYPE" = "debug" ]; then
+    if [ -n "$lang_c" ]; then
+        echo '${target.cFlags} += ${DEBUG_CC_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+    if [ -n "$lang_cpp" ]; then
+        echo '${target.cxxFlags} += ${DEBUG_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
 fi
-if [ ! -z "${LDFLAGS}" ]; then
-    echo "${target.getLDFlags()} += $LDFLAGS" >> $TEMP_DIR/config.mk
+if [ "$BUILD_TYPE" = "release" ]; then
+    if [ -n "$lang_c" ]; then
+        echo '${target.cFlags} += ${RELEASE_CC_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+    if [ -n "$lang_cpp" ]; then
+        echo '${target.cxxFlags} += ${RELEASE_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk"
+    fi
+fi
+if [ -n "${TEMP_LDFLAGS}" ]; then
+    echo "${target.ldFlags} += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk"
 fi
 
 #end
+
+# final result
 if [ $ERROR -ne 0 ]; then
-	echo
-	echo "Error: Unresolved dependencies"
-	echo $DEPENDENCIES_FAILED
-	rm -Rf $TEMP_DIR
-	exit 1
+    echo
+    echo "Error: Unresolved dependencies"
+    echo "$DEPENDENCIES_FAILED"
+    abort_configure
 fi
 
 echo "configure finished"
 echo
 echo "Build Config:"
-echo "  PREFIX:    $PREFIX"
-echo "  TOOLCHAIN: $TOOLCHAIN_NAME"
+echo "  PREFIX:      $prefix"
+echo "  TOOLCHAIN:   $TOOLCHAIN_NAME"
 #if ( $options.size() > 0 )
 echo "Options:"
-cat $TEMP_DIR/options
+cat "$TEMP_DIR/options"
+#end
+#if ( $features.size() > 0 )
+echo "Features:"
+#foreach( $feature in $features )
+if [ -n "${D}${feature.varName}" ]; then
+echo "  $feature.name: on"
+else
+echo "  $feature.name: off"
+fi
+#end
 #end
 echo
-cat $TEMP_DIR/config.mk $TEMP_DIR/make.mk > config.mk
-rm -Rf $TEMP_DIR
 
+# generate the config.mk file
+cat > "$TEMP_DIR/config.mk" << __EOF__
+#
+# config.mk generated by configure
+#
 
+__EOF__
+write_toolchain_defaults "$TEMP_DIR/toolchain.mk"
+cat "$TEMP_DIR/vars.mk" "$TEMP_DIR/toolchain.mk" "$TEMP_DIR/flags.mk" "$TEMP_DIR/make.mk" > config.mk
+rm -Rf "$TEMP_DIR"
--- a/make/osx.mk	Mon Feb 12 21:13:23 2024 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-#
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
-#
-# Copyright 2011 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.
-#
-
-CC = gcc
-LD = gcc
-AR = ar
-RM = rm
-
-CFLAGS  += -std=gnu99 -g -I/usr/include/libxml2
-LDFLAGS += -lxml2 -lz -lpthread -licucore -lm
-ARFLAGS = -r
-RMFLAGS = -f
-
-OBJ_EXT = o
-LIB_EXT = a
-APP_EXT =
-
-PACKAGE_SCRIPT = package_osx.sh
--- a/make/project.xml	Mon Feb 12 21:13:23 2024 +0100
+++ b/make/project.xml	Sun Jun 09 15:43:08 2024 +0200
@@ -1,5 +1,51 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project>
+<project xmlns="http://unixwork.de/uwproj">
+	<dependency>
+		<lang>c</lang>
+	</dependency>
+	
+	<dependency name="curl" platform="macos">
+		<cflags exec="true">curl-config --cflags</cflags>
+		<ldflags exec="true">curl-config --ldflags</ldflags>
+	</dependency>
+	<dependency name="curl">
+		<pkgconfig>libcurl</pkgconfig>
+	</dependency>
+	<dependency name="curl">
+		<cflags exec="true">curl-config --cflags</cflags>
+		<ldflags exec="true">curl-config --libs</ldflags>
+	</dependency>
+	
+	<dependency name="libxml2" platform="windows">
+		<cflags exec="true">xml2-config --cflags</cflags>
+		<ldflags exec="true">xml2-config --libs</ldflags>
+	</dependency>
+	<dependency name="libxml2" platform="macos">
+		<cflags exec="true">xml2-config --cflags</cflags>
+		<ldflags exec="true">xml2-config --libs</ldflags>
+	</dependency>
+	<dependency name="libxml2">
+		<pkgconfig>libxml-2.0</pkgconfig>
+	</dependency>
+	<dependency name="libxml2">
+		<cflags exec="true">xml2-config --cflags</cflags>
+		<ldflags exec="true">xml2-config --libs</ldflags>
+	</dependency>
+	
+	<dependency name="openssl" platform="windows">
+		<ldflags>-lssl -lcrypto</ldflags>
+	</dependency>
+	<dependency name="openssl" platform="macos">
+		<ldflags>-framework CoreFoundation</ldflags>
+	</dependency>
+	<dependency name="openssl" platform="bsd" not="macos">
+		<ldflags>-lssl -lcrypto</ldflags>
+	</dependency>
+	<dependency name="openssl">
+		<pkgconfig>openssl</pkgconfig>
+	</dependency>
+	
+
 	<dependency name="gtk4">
 		<pkgconfig>gtk+-4.0</pkgconfig>
 		<cflags>-DUI_GTK3</cflags>
@@ -24,18 +70,22 @@
 	<dependency name="winui" platform="windows">
 		<cflags>-DUI_WINUI</cflags>
 	</dependency>
+	
+	<!--
 	<dependency name="qt4">
 		<test>which qmake-qt4</test>
-		<cflags type="exec">qmake-qt4 -o - /dev/null | grep DEFINES\ </cflags>
-		<cflags type="exec">qmake-qt4 -o - /dev/null | grep INCPATH\ </cflags>
-		<ldflags type="exec"><cflags type="exec">qmake-qt4 -o - /dev/null | grep LIBS\ </cflags></ldflags>
+		<cflags exec="true">qmake-qt4 -o - /dev/null | grep DEFINES\ </cflags>
+		<cflags exec="true">qmake-qt4 -o - /dev/null | grep INCPATH\ </cflags>
+		<ldflags exec="true">qmake-qt4 -o - /dev/null | grep LIBS\ </ldflags>
 	</dependency>
+	
 	<dependency name="qt5">
 		<test>which qmake-qt5</test>
-		<cflags type="exec">qmake-qt5 -o - /dev/null | grep DEFINES\ </cflags>
-		<cflags type="exec">qmake-qt5 -o - /dev/null | grep INCPATH\ </cflags>
-		<ldflags type="exec"><cflags type="exec">qmake-qt5 -o - /dev/null | grep LIBS\ </cflags></ldflags>
+		<cflags exec="true">qmake-qt5 -o - /dev/null | grep DEFINES\ </cflags>
+		<cflags exec="true">qmake-qt5 -o - /dev/null | grep INCPATH\ </cflags>
+		<ldflags exec="true">qmake-qt5 -o - /dev/null | grep LIBS\ </ldflags>
 	</dependency>
+	-->
 	<dependency name="cocoa" platform="macos">
 		<cflags>-DUI_COCOA</cflags>
 		<ldflags>-lobjc -framework Cocoa</ldflags>
@@ -52,12 +102,12 @@
 	</dependency>
 	
 	<dependency platform="macos">
-		<make>OBJ_EXT = .o</make>
+		<make>OBJ_EXT = o</make>
 		<make>LIB_EXT = .a</make>
 		<make>PACKAGE_SCRIPT = package_osx.sh</make>
 	</dependency>
 	<dependency platform="unix" not="macos">
-		<make>OBJ_EXT = .o</make>
+		<make>OBJ_EXT = o</make>
 		<make>LIB_EXT = .a</make>
 		<make>PACKAGE_SCRIPT = package_unix.sh</make>
 	</dependency>
@@ -67,6 +117,10 @@
 		<ldflags>-L/usr/local/lib</ldflags>
 	</dependency>
 	
+	<target name="dav">
+		<dependencies>curl,libxml2,openssl</dependencies>
+	</target>
+	
 	<target name="tk">
 		<option arg="toolkit">
 			<value str="gtk4">
--- a/make/toolchain.sh	Mon Feb 12 21:13:23 2024 +0100
+++ b/make/toolchain.sh	Sun Jun 09 15:43:08 2024 +0200
@@ -3,179 +3,198 @@
 # toolchain detection
 #
 
-C_COMPILERS="cc gcc clang suncc"
-CPP_COMPILERS="CC g++ clang++ sunCC"
-unset CC_ARG_CHECKED
-unset TOOLCHAIN_DETECTION_ERROR
+if isplatform "bsd" && notisplatform "openbsd"; then
+  C_COMPILERS="clang gcc cc"
+  CPP_COMPILERS="clang++ g++ CC"
+else
+  C_COMPILERS="gcc clang suncc cc"
+  CPP_COMPILERS="g++ clang++ sunCC CC"
+fi
+unset TOOLCHAIN
 unset TOOLCHAIN_NAME
+unset TOOLCHAIN_CC
+unset TOOLCHAIN_CXX
 
 check_c_compiler()
 {
-	cat > $TEMP_DIR/test.c << __EOF__
+  cat > "$TEMP_DIR/test.c" << __EOF__
 /* test file */
 #include <stdio.h>
 int main(int argc, char **argv) {
-#if defined(__clang__)
-	printf("clang\n");
+#if defined(_MSC_VER)
+  printf("msc\n");
+#elif defined(__clang__)
+  printf("clang gnuc\n");
 #elif defined(__GNUC__)
-	printf("gcc\n");
+  printf("gcc gnuc\n");
 #elif defined(__sun)
-	printf("suncc\n");
+  printf("suncc\n");
 #else
-	printf("unknown\n");
+  printf("unknown\n");
 #endif
-	return 0;
+  return 0;
 }
 __EOF__
-	rm -f $TEMP_DIR/checkcc
-	$1 -o $TEMP_DIR/checkcc $CFLAGS $LDFLAGS $TEMP_DIR/test.c 2> /dev/null
-	
-	if [ $? -ne 0 ]; then
-		return 1
-	fi
-	return 0
+  rm -f "$TEMP_DIR/checkcc"
+  $1 -o "$TEMP_DIR/checkcc" $CFLAGS $LDFLAGS "$TEMP_DIR/test.c" 2> /dev/null
 }
 
 check_cpp_compiler()
 {
-	cat > $TEMP_DIR/test.cpp << __EOF__
+  cat > "$TEMP_DIR/test.cpp" << __EOF__
 /* test file */
 #include <iostream>
 int main(int argc, char **argv) {
-#if defined(__clang__)
-	std::cout << "clang" << std::endl;
+#if defined(_MSC_VER)
+  std::cout << "msc" << std::endl;
+#elif defined(__clang__)
+  std::cout << "clang gnuc" << std::endl;
 #elif defined(__GNUC__)
-	std::cout << "gcc" << std::endl;
+  std::cout << "gcc gnuc" << std::endl;
 #elif defined(__sun)
-	std::cout << "suncc" << std::endl;
+  std::cout << "suncc" << std::endl;
 #else
-	std::cout << "unknown" << std::endl;
+  std::cout << "cc" << std::endl;
 #endif
-	return 0;
+  return 0;
+}
+__EOF__
+  rm -f "$TEMP_DIR/checkcc"
+  $1 -o "$TEMP_DIR/checkcc" $CXXFLAGS $LDFLAGS "$TEMP_DIR/test.cpp" 2> /dev/null
+}
+
+create_libtest_source()
+{
+  # $1: filename
+  # $2: optional include
+  cat > "$TEMP_DIR/$1" << __EOF__
+/* libtest file */
+int main(int argc, char **argv) {
+  return 0;
 }
 __EOF__
-	rm -f $TEMP_DIR/checkcc
-	$1 -o $TEMP_DIR/checkcc $CXXFLAGS $LDFLAGS $TEMP_DIR/test.cpp 2> /dev/null
-	
-	if [ $? -ne 0 ]; then
-		return 1
-	fi
-	return 0
+  if [ -n "$2" ]; then
+    echo "#include <$2>" >> "$TEMP_DIR/$1"
+  fi
+}
+
+check_c_lib()
+{
+  # $1: libname
+  # $2: optional include
+  if [ -z "$TOOLCHAIN_CC" ]; then
+    return 1
+  fi
+  create_libtest_source "test.c" "$2"
+  rm -f "$TEMP_DIR/checklib"
+  $TOOLCHAIN_CC -o "$TEMP_DIR/checklib" $CFLAGS $LDFLAGS "-l$1" "$TEMP_DIR/test.c" 2> /dev/null
+}
+
+check_cpp_lib()
+{
+  # $1: libname
+  # $2: optional include
+  if [ -z "$TOOLCHAIN_CXX" ]; then
+    return 1
+  fi
+  create_libtest_source "test.cpp" "$2"
+  rm -f "$TEMP_DIR/checklib"
+  $TOOLCHAIN_CXX -o "$TEMP_DIR/checklib" $CXXFLAGS $LDFLAGS "-l$1" "$TEMP_DIR/test.cpp" 2> /dev/null
+}
+
+check_lib()
+{
+  # $1: libname
+  # $2: optional include
+  if [ -n "$TOOLCHAIN_CC" ]; then
+    check_c_lib "$1" "$2"
+  elif  [ -n "$TOOLCHAIN_CXX" ]; then
+    check_cpp_lib "$1" "$2"
+  fi
 }
 
-printf "detect C compiler... "
+detect_c_compiler()
+{
+  if [ -n "$TOOLCHAIN_CC" ]; then
+    return 0
+  fi
+  printf "detect C compiler... "
+  if [ -n "$CC" ]; then
+    if check_c_compiler "$CC"; then
+      TOOLCHAIN_CC=$CC
+      TOOLCHAIN=`"$TEMP_DIR/checkcc"`
+      TOOLCHAIN_NAME=`echo "$TOOLCHAIN" | cut -f1 -d' ' -`
+      echo "$CC"
+      return 0
+    else
+      echo "$CC is not a working C compiler"
+      return 1
+    fi
+  else
+    for COMP in $C_COMPILERS
+    do
+      if check_c_compiler "$COMP"; then
+        TOOLCHAIN_CC=$COMP
+        TOOLCHAIN=`"$TEMP_DIR/checkcc"`
+        TOOLCHAIN_NAME=`echo "$TOOLCHAIN" | cut -f1 -d' ' -`
+        echo "$COMP"
+        return 0
+      fi
+    done
+    echo "not found"
+    return 1
+  fi
+}
 
-for COMP in $C_COMPILERS
-do
-	check_c_compiler $COMP
-	if [ $? -ne 0 ]; then
-		if [ ! -z "$CC" ]; then
-			if [ $COMP = $CC ]; then
-				echo "$CC is not a working C Compiler"
-				TOOLCHAIN_DETECTION_ERROR="error"
-				break
-			fi
-		fi
-	else
-		TOOLCHAIN_NAME=`$TEMP_DIR/checkcc`
-		USE_TOOLCHAIN=$TOOLCHAIN_NAME
-		if [ $COMP = "cc" ]; then
-			# we have found a working compiler, but in case
-			# the compiler is gcc or clang, we try to use
-			# these commands and not 'cc'
-			TOOLCHAIN_NAME=`$TEMP_DIR/checkcc`
-			if [ $TOOLCHAIN_NAME = "gcc" ]; then
-				check_c_compiler "gcc"
-				if [ $? -eq 0 ]; then
-					COMP=gcc
-					USE_TOOLCHAIN="gcc"
-				fi
-			fi
-			if [ $TOOLCHAIN_NAME = "clang" ]; then
-				check_c_compiler "clang"
-				if [ $? -eq 0 ]; then
-					COMP=clang
-					USE_TOOLCHAIN="clang"
-				fi
-			fi
-		fi
-		
-		TOOLCHAIN_NAME=$USE_TOOLCHAIN
-		TOOLCHAIN_CC=$COMP
-		echo $COMP
-		break
-	fi
-done
-if [ -z $TOOLCHAIN_CC ]; then
-	echo "not found"
-fi
-
-printf "detect C++ compiler... "
+detect_cpp_compiler()
+{
+  if [ -n "$TOOLCHAIN_CXX" ]; then
+    return 0
+  fi
+  printf "detect C++ compiler... "
 
-for COMP in $CPP_COMPILERS
-do
-	check_cpp_compiler $COMP
-	if [ $? -ne 0 ]; then
-		if [ ! -z "$CXX" ]; then
-			if [ $COMP = $CXX ]; then
-				echo "$CC is not a working C++ Compiler"
-				TOOLCHAIN_DETECTION_ERROR="error"
-				break
-			fi
-		fi
-	else
-		if [ $COMP = "CC" ]; then
-			# we have found a working compiler, but in case
-			# the compiler is gcc or clang, we try to use
-			# these commands and not 'cc'
-			TOOLCHAIN_NAME=`$TEMP_DIR/checkcc`
-			USE_TOOLCHAIN=$TOOLCHAIN_NAME
-			if [ $TOOLCHAIN_NAME = "gcc" ]; then
-				check_cpp_compiler "g++"
-				if [ $? -eq 0 ]; then
-				   COMP=g++
-				   USE_TOOLCHAIN="gcc"
-				fi
-			fi
-			if [ $TOOLCHAIN_NAME = "clang" ]; then
-				check_cpp_compiler "clang++"
-				if [ $? -eq 0 ]; then
-				   COMP=clang++
-				   USE_TOOLCHAIN="clang"
-				fi
-			fi
-		fi
-		
-		TOOLCHAIN_NAME=$USE_TOOLCHAIN
-		TOOLCHAIN_CXX=$COMP
-		echo $COMP
-		break
-	fi
-done
-if [ -z $TOOLCHAIN_CXX ]; then
-	echo "not found"
-fi
+  if [ -n "$CXX" ]; then
+    if check_cpp_compiler "$CXX"; then
+      TOOLCHAIN_CXX=$CXX
+      TOOLCHAIN=`"$TEMP_DIR/checkcc"`
+      TOOLCHAIN_NAME=`echo "$TOOLCHAIN" | cut -f1 -d' ' -`
+      echo "$CXX"
+      return 0
+    else
+      echo "$CXX is not a working C++ compiler"
+      return 1
+    fi
+  else
+    for COMP in $CPP_COMPILERS
+    do
+      if check_cpp_compiler "$COMP"; then
+        TOOLCHAIN_CXX=$COMP
+        TOOLCHAIN=`"$TEMP_DIR/checkcc"`
+        TOOLCHAIN_NAME=`echo "$TOOLCHAIN" | cut -f1 -d' ' -`
+        echo "$COMP"
+        return 0
+      fi
+    done
+    echo "${TOOLCHAIN_CXX:-"not found"}"
+    return 1
+  fi
+}
 
-TOOLCHAIN_LD=$TOOLCHAIN_CC
-
-if [ -z "$TOOLCHAIN_NAME" ]; then
-	TOOLCHAIN_DETECTION_ERROR="error"
-else
-	cat >> $TEMP_DIR/config.mk << __EOF__
-# toolchain
-__EOF__
-	echo "CC = ${TOOLCHAIN_CC}" >> $TEMP_DIR/config.mk
-	if [ ! -z "$TOOLCHAIN_CXX" ]; then
-		echo "CXX = ${TOOLCHAIN_CXX}" >> $TEMP_DIR/config.mk
-	fi
-	echo "LD = ${TOOLCHAIN_LD}" >> $TEMP_DIR/config.mk
-	echo >> $TEMP_DIR/config.mk
-	
-	cat "make/${TOOLCHAIN_NAME}.mk" > /dev/null 2>&1
-	if [ $? -eq 0 ]; then 
-		echo "include \$(BUILD_ROOT)/make/${TOOLCHAIN_NAME}.mk" >> $TEMP_DIR/config.mk
-	else
-		echo "SHLIB_CFLAGS = -fPIC" >> $TEMP_DIR/config.mk
-		echo "SHLIB_LDFLAGS = -shared" >> $TEMP_DIR/config.mk
-	fi
-fi
+write_toolchain_defaults()
+{
+  echo "# toolchain" >> "$1"
+  if [ -n "$TOOLCHAIN_CC" ]; then
+    echo "CC = ${TOOLCHAIN_CC}" >> "$1"
+  fi
+  if [ -n "$TOOLCHAIN_CXX" ]; then
+    echo "CXX = ${TOOLCHAIN_CXX}" >> "$1"
+  fi
+  echo >> "$1"
+  if [ -f "make/${TOOLCHAIN_NAME}.mk" ]; then
+    cat "make/${TOOLCHAIN_NAME}.mk" >> "$1"
+  elif [ -f "make/cc.mk" ]; then
+    cat "make/cc.mk" >> "$1"
+  else
+    echo "!!! WARNING !!! Default toolchain flags not found. Configuration might be incomplete."
+  fi
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/make/uwproj.xsd	Sun Jun 09 15:43:08 2024 +0200
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns="http://unixwork.de/uwproj"
+           targetNamespace="http://unixwork.de/uwproj"
+           elementFormDefault="qualified"
+           version="0.2"
+>
+    <xs:element name="project" type="ProjectType"/>
+
+    <xs:complexType name="ProjectType">
+        <xs:annotation>
+            <xs:documentation>
+                The root element of an uwproj project.
+                Consists of an optional <code>config</code> element
+                and an arbitrary number of <code>dependency</code>
+                and <code>target</code> elements.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="config" type="ConfigType" minOccurs="0"/>
+            <xs:element name="dependency" type="DependencyType" minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="target" type="TargetType" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="ConfigType">
+        <xs:annotation>
+            <xs:documentation>
+                The configuration section.
+                Consists of an arbitrary number of <code>var</code> elements.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="var" type="ConfigVarType" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="ConfigVarType">
+        <xs:annotation>
+            <xs:documentation>
+                The definition of a configuration variable.
+                <p>
+                    Configuration variables are supposed to be used in the configure script and are also
+                    written to the resulting config file (in contrast to make variables, which are only
+                    written to the config file).
+                    The <code>name</code> attribute is mandatory, the value is defined by the text body of the element.
+                    The optional Boolean <code>exec</code> attribute (false by default) controls, whether the entire
+                    definition is automatically executed under command substitution.
+                </p>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="name" type="xs:string" use="required"/>
+                <xs:attribute name="exec" type="xs:boolean" default="false"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:complexType name="PkgConfigType">
+        <xs:annotation>
+            <xs:documentation>
+                Instructs configure to invoke <code>pkg-config</code>, if present on the system, to determine
+                compiler and linker flags. The text body of this element defines the package name to search.
+                To constrain the allowed versions, use the attributes <code>atleast, exact, max</code>.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="atleast" type="xs:string"/>
+                <xs:attribute name="exact" type="xs:string"/>
+                <xs:attribute name="max" type="xs:string"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:simpleType name="LangType">
+        <xs:annotation>
+            <xs:documentation>
+                Requests a compiler for the specified language. Allowed values are
+                c, cpp.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="c"/>
+            <xs:enumeration value="cpp"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="DependencyType">
+        <xs:annotation>
+            <xs:documentation>
+                Declares a dependency.
+                <p>
+                    If the optional <code>name</code> attribute is omitted, the dependency is global
+                    and must be satisfied, otherwise configuration shall fail.
+                    A <em>named dependency</em> can be referenced by a target (or is implicitly referenced
+                    by the default target, if no targets are specified).
+                    Multiple declarations for the same named dependency may exist, in which case each declaration
+                    is checked one after another, until one block is satisfied. The result of the first satisfied
+                    dependency declaration is supposed to be applied to the config file.
+                </p>
+                <p>
+                    The optional <code>platform</code> attribute may specify a <em>single</em> platform identifier and
+                    the optional <code>toolchain</code> attribute may specify a <em>single</em> toolchain.
+                    The optional <code>not</code> attribute may specify a comma-separated list of platform and/or
+                    toolchain identifiers.
+                    The configure script shall skip this dependency declaration if the detected platform and toolchain
+                    is not matching the filter specification of these attributes.
+                </p>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="lang" type="LangType"/>
+            <xs:element name="cflags" type="FlagsType"/>
+            <xs:element name="cxxflags" type="FlagsType"/>
+            <xs:element name="ldflags" type="FlagsType"/>
+            <xs:element name="pkgconfig" type="PkgConfigType"/>
+            <xs:element name="test" type="xs:string">
+                <xs:annotation>
+                    <xs:documentation>
+                        Specifies a custom command that shall be executed to test whether this dependency is satisfied.
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:element>
+            <xs:element name="make" type="MakeVarType"/>
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string"/>
+        <xs:attribute name="platform" type="xs:string"/>
+        <xs:attribute name="toolchain" type="xs:string"/>
+        <xs:attribute name="not" type="xs:string"/>
+    </xs:complexType>
+
+    <xs:complexType name="FlagsType">
+        <xs:annotation>
+            <xs:documentation>
+                Instructs configure to append the contents of the element's body to the respective flags variable.
+                If the optional <code>exec</code> flag is set to <code>true</code>, the contents are supposed to be
+                executed under command substitution <em>at configuration time</em> before they are applied.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="exec" type="xs:boolean" default="false"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:complexType name="TargetType">
+        <xs:annotation>
+            <xs:documentation>
+                Declares a build target that is supposed to be configured.
+                <p>
+                    If no build target is declared explicitly, an implicit default
+                    target is generated, which has the <code>alldependencies</code>
+                    flag set.
+                </p>
+                <p>
+                    The optional <code>name</code> attribute is also used to generate a prefix
+                    for the compiler and linker flags variables.
+                    Furthermore, a target may consist of an arbitrary number of <code>feature</code>,
+                    <code>option</code>, and <code>define</code> elements.
+                    Named dependencies can be listed (separated by comma) in the <code>dependencies</code>
+                    element. If this target shall use <em>all</em> available named dependencies, the empty
+                    element <code>alldependencies</code> can be used as a shortcut.
+                </p>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="feature" type="FeatureType"/>
+            <xs:element name="option" type="OptionType"/>
+            <xs:element name="define" type="DefineType"/>
+            <xs:element name="dependencies" type="DependenciesType"/>
+            <xs:element name="alldependencies">
+                <xs:complexType/>
+            </xs:element>
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string"/>
+    </xs:complexType>
+
+    <xs:complexType name="FeatureType">
+        <xs:annotation>
+            <xs:documentation>
+                Declares an optional feature, that can be enabled during configuration, if all
+                <code>dependencies</code> are satisfied.
+                If a feature is enabled, all <code>define</code> and <code>make</code> definitions are
+                supposed to be applied to the config file.
+                In case the optional <code>default</code> attribute is set to true, the feature is enabled by default
+                and is supposed to be automatically disabled (without error) when the dependencies are not satisfied.
+                The name that is supposed to be used for the --enable and --disable arguments can be optionally
+                specified with the <code>arg</code> attribute. Otherwise, the <code>name</code> is used by default.
+                Optionally, a description for the help text of the resulting configure script can be specified by
+                adding a <code>desc</code> element.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:group ref="TargetDataGroup"/>
+        </xs:choice>
+        <xs:attribute name="name" type="xs:string" use="required"/>
+        <xs:attribute name="arg" type="xs:string"/>
+        <xs:attribute name="default" type="xs:boolean" default="false"/>
+        <xs:element name="desc" type="xs:string"/>
+    </xs:complexType>
+
+    <xs:complexType name="OptionType">
+        <xs:annotation>
+            <xs:documentation>
+                Declares a configuration option.
+                The option argument name is specified with the <code>arg</code> attribute.
+                Then, the children of this element specify possible <code>values</code> by defining the conditions
+                (in terms of dependencies) and effects (in terms of defines and make variables) of each value.
+                Finally, a set of <code>default</code>s is specified which supposed to automagically select the most
+                appropriate value for a specific platform under the available dependencies (in case the option is not
+                explicitly specified by using the command line argument).
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="value" type="OptionValueType" minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="default" type="OptionDefaultType" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="arg" type="xs:string" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="OptionValueType">
+        <xs:annotation>
+            <xs:documentation>
+                Declares a possible value for the option (in the <code>str</code> attribute) and
+                the conditions (<code>dependencies</code>) and effects, the value has.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:group ref="TargetDataGroup"/>
+        </xs:choice>
+        <xs:attribute name="str" type="xs:string" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="OptionDefaultType">
+        <xs:annotation>
+            <xs:documentation>
+                Specifies a default value for this option. Multiple default values can be specified, in which case
+                they are checked one after another for availability. With the optional <code>platform</code> attribute,
+                the default value can be constrained to a <em>single</em> specific platform and is supposed to be
+                skipped by configure, when this platform is not detected.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="value" type="xs:string" use="required"/>
+        <xs:attribute name="platform" type="xs:string"/>
+    </xs:complexType>
+
+    <xs:group name="TargetDataGroup">
+        <xs:choice>
+            <xs:element name="define" type="DefineType" minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="dependencies" type="DependenciesType" minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="make" type="MakeVarType" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:choice>
+    </xs:group>
+
+    <xs:complexType name="DefineType">
+        <xs:annotation>
+            <xs:documentation>
+                Specifies C/C++ pre-processor definitions that are supposed to
+                be appended to the compiler flags, if supported.
+                (Note: for example, Fortran also supports C/C++ style pre-processor definitions under
+                certain circumstances)
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="name" type="xs:string" use="required"/>
+        <xs:attribute name="value" type="xs:string"/>
+    </xs:complexType>
+
+    <xs:simpleType name="DependenciesType">
+        <xs:annotation>
+            <xs:documentation>A comma-separated list of named dependencies.</xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string"/>
+    </xs:simpleType>
+
+    <xs:simpleType name="MakeVarType">
+        <xs:annotation>
+            <xs:documentation>
+                The text contents in the body of this element are supposed to be appended literally
+                to the config file without prior processing.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string"/>
+    </xs:simpleType>
+</xs:schema>
--- a/make/windows.mk	Mon Feb 12 21:13:23 2024 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-#
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
-#
-# Copyright 2011 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.
-#
-
-CC = gcc
-LD = gcc
-AR = ar
-RM = rm
-
-CFLAGS  = -std=gnu99
-LDFLAGS = 
-ARFLAGS = -r
-RMFLAGS = -f
-
-OBJ_EXT = obj
-LIB_EXT = lib
-APP_EXT = .exe
-
--- a/ucx/Makefile	Mon Feb 12 21:13:23 2024 +0100
+++ b/ucx/Makefile	Sun Jun 09 15:43:08 2024 +0200
@@ -32,7 +32,7 @@
 # list of source files
 SRC  = allocator.c
 SRC += array_list.c
-SRC += basic_mempool.c
+SRC += mempool.c
 SRC += buffer.c
 SRC += compare.c
 SRC += hash_key.c
--- a/ui/common/objs.mk	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/common/objs.mk	Sun Jun 09 15:43:08 2024 +0200
@@ -38,6 +38,7 @@
 COMMON_OBJ += menu.o
 COMMON_OBJ += toolbar.o
 COMMON_OBJ += ucx_properties.o
+COMMON_OBJ += threadpool.o
 
 TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)%)
 TOOLKITSOURCE += $(COMMON_OBJ:%.o=common/%.c)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/common/threadpool.c	Sun Jun 09 15:43:08 2024 +0200
@@ -0,0 +1,174 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 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 "threadpool.h"
+
+#include <pthread.h>
+
+#ifndef _WIN32
+
+
+static threadpool_job kill_job;
+
+UiThreadpool* threadpool_new(int min, int max) {
+    UiThreadpool *pool = malloc(sizeof(UiThreadpool));
+    pool->queue = NULL;
+    pool->queue_len = 0;
+    pool->num_idle = 0;
+    pool->min_threads = min;
+    pool->max_threads = max;
+
+    pthread_mutex_init(&pool->queue_lock, NULL);
+    pthread_mutex_init(&pool->avlbl_lock, NULL);
+    pthread_cond_init(&pool->available, NULL);  
+
+    return pool;
+}
+
+int threadpool_start(UiThreadpool *pool) {
+    /* create pool threads */
+    for(int i=0;i<pool->min_threads;i++) {
+        pthread_t t;
+        if (pthread_create(&t, NULL, threadpool_func, pool) != 0) {
+            fprintf(stderr, "uic: threadpool_start: pthread_create failed: %s", strerror(errno));
+            return 1;
+        }
+    }
+    return 0;
+}
+
+void* threadpool_func(void *data) {
+    UiThreadpool *pool = (UiThreadpool*)data;
+    
+    for(;;) {
+        threadpool_job *job = threadpool_get_job(pool);
+        if(job == &kill_job) {
+            break;
+        }
+
+        job->callback(job->data);
+
+        free(job);
+    }
+    return NULL;
+}
+
+threadpool_job* threadpool_get_job(UiThreadpool *pool) {
+    pthread_mutex_lock(&pool->queue_lock);
+
+    threadpool_job *job = NULL;
+    pool->num_idle++;
+    while(job == NULL) {
+        if(pool->queue_len == 0) {
+            pthread_cond_wait(&pool->available, &pool->queue_lock);
+            continue;
+        } else {
+            pool_queue_t *q = pool->queue;
+            job = q->job;
+            pool->queue = q->next;
+            pool->queue_len--;
+            free(q);
+        }
+    }
+    pool->num_idle--;
+
+    pthread_mutex_unlock(&pool->queue_lock);
+    return job;
+}
+
+void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data) {
+    // TODO: handle errors
+    
+    threadpool_job *job = malloc(sizeof(threadpool_job));
+    job->callback = func;
+    job->data = data;
+
+    pthread_mutex_lock(&pool->queue_lock);
+    threadpool_enqueue_job(pool, job);
+
+    int create_thread = 0;
+    int destroy_thread = 0;
+    int diff = pool->queue_len - pool->num_idle;
+    
+    //if(pool->queue_len == 1) {
+    pthread_cond_signal(&pool->available);
+    //}
+    
+    pthread_mutex_unlock(&pool->queue_lock);
+}
+
+void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job) {
+    pool_queue_t *q = malloc(sizeof(pool_queue_t));
+    q->job = job;
+    q->next = NULL;
+    
+    if(pool->queue == NULL) {
+        pool->queue = q;
+    } else {
+        pool_queue_t *last_elem = pool->queue;
+        while(last_elem->next != NULL) {
+            last_elem = last_elem->next;
+        }
+        last_elem->next = q;
+    }
+    pool->queue_len++;
+}
+
+
+
+
+
+
+UiThreadpool* ui_threadpool_create(int nthreads) {
+    return threadpool_new(nthreads, nthreads);
+}
+
+void ui_threadpool_destroy(UiThreadpool* pool) {
+    
+}
+
+static void* ui_threadpool_job_func(void *data) {
+    UiJob *job = data;
+    
+    free(job);
+    return NULL;
+}
+
+void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) {
+    UiJob* job = malloc(sizeof(UiJob));
+    job->obj = obj;
+    job->job_func = tf;
+    job->job_data = td;
+    job->finish_callback = f;
+    job->finish_data = fd;
+    threadpool_run(pool, ui_threadpool_job_func, job);
+}
+
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/common/threadpool.h	Sun Jun 09 15:43:08 2024 +0200
@@ -0,0 +1,85 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2024 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UIC_THREADPOOL_H
+#define UIC_THREADPOOL_H
+
+#include "../ui/toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+    
+typedef struct UiJob {
+    UiObject      *obj;
+    ui_threadfunc job_func;
+    void          *job_data;
+    ui_callback   finish_callback;
+    void          *finish_data;
+} UiJob;
+    
+    
+typedef struct  _threadpool_job   threadpool_job;
+typedef void*(*job_callback_f)(void *data);
+    
+typedef struct _pool_queue pool_queue_t;
+struct UiThreadpool {
+    pthread_mutex_t queue_lock;
+    pthread_mutex_t avlbl_lock;
+    pthread_cond_t  available;
+    pool_queue_t    *queue;
+    uint32_t        queue_len;
+    uint32_t        num_idle;
+    int             min_threads;
+    int             max_threads;
+};
+
+struct _threadpool_job {
+    job_callback_f      callback;
+    void                *data;
+};
+
+struct _pool_queue {
+    threadpool_job   *job;
+    pool_queue_t     *next;
+};
+
+UiThreadpool* threadpool_new(int min, int max);
+int threadpool_start(UiThreadpool *pool);
+void* threadpool_func(void *data);
+threadpool_job* threadpool_get_job(UiThreadpool *pool);
+void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data);
+void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_THREADPOOL_H */
+
--- a/ui/gtk/button.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/button.c	Sun Jun 09 15:43:08 2024 +0200
@@ -35,15 +35,41 @@
 #include "../common/context.h"
 #include "../common/object.h"
 
-UIWIDGET ui_button_deprecated(UiObject *obj, char *label, ui_callback f, void *data) {
-    GtkWidget *button = gtk_button_new_with_label(label);
+void ui_button_set_icon_name(GtkWidget *button, const char *icon) {
+    if(!icon) {
+        return;
+    }
     
-    if(f) {
+#ifdef UI_GTK4
+    gtk_button_set_icon_name(GTK_BUTTON(button), icon);
+#else
+#if GTK_CHECK_VERSION(2, 6, 0)
+    GtkWidget *image = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON);
+    if(image) {
+        gtk_button_set_image(GTK_BUTTON(button), image);
+    }
+#else
+    // TODO
+#endif
+#endif
+}
+
+UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    GtkWidget *button = gtk_button_new();
+    if(args.label) {
+        gtk_button_set_label(GTK_BUTTON(button), args.label);
+    }
+    ui_button_set_icon_name(button, args.icon);
+
+    
+    if(args.onclick) {
         UiEventData *event = malloc(sizeof(UiEventData));
         event->obj = obj;
-        event->userdata = data;
-        event->callback = f;
+        event->userdata = args.onclickdata;
+        event->callback = args.onclick;
         event->value = 0;
+        event->customdata = NULL;
 
         g_signal_connect(
                 button,
@@ -57,8 +83,8 @@
                 event);
     }
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, button, FALSE);
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, button, FALSE);
     
     return button;
 }
@@ -98,66 +124,90 @@
     ui_notify_evt(i->observers, &e);
 }
 
-UIWIDGET ui_checkbox_var(UiObject *obj, char *label, UiVar *var) {
-    GtkWidget *button = gtk_check_button_new_with_label(label);
+static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleArgs args) {
+    UiObject* current = uic_current_obj(obj);
     
-    // bind value
-    if(var) {
-        UiInteger *value = var->value;
-        value->obj = GTK_TOGGLE_BUTTON(button);
+    if(args.label) {
+        gtk_button_set_label(GTK_BUTTON(widget), args.label);
+    }
+    ui_button_set_icon_name(widget, args.icon);
+    
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER);
+    if (var) {
+        UiInteger* value = (UiInteger*)var->value;
+        value->obj = widget;
         value->get = ui_toggle_button_get;
         value->set = ui_toggle_button_set;
-        gtk_toggle_button_set_active(value->obj, value->value);
         
         UiVarEventData *event = malloc(sizeof(UiVarEventData));
         event->obj = obj;
         event->var = var;
         event->observers = NULL;
+        event->callback = NULL;
+        event->userdata = NULL;
 
         g_signal_connect(
-                button,
+                widget,
                 "clicked",
                 G_CALLBACK(ui_toggled_obs),
                 event);
         g_signal_connect(
-                button,
+                widget,
                 "destroy",
                 G_CALLBACK(ui_destroy_vardata),
                 event);
     }
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, button, FALSE);
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, widget, FALSE);
     
-    return button;
+    return widget;
+}
+
+UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) {
+    return togglebutton_create(obj, gtk_toggle_button_new(), args);
 }
 
-UIWIDGET ui_checkbox_deprecated(UiObject *obj, char *label, UiInteger *value) {
-    UiVar *var = NULL;
-    if(value) {
-        var = malloc(sizeof(UiVar));
-        var->value = value;
-        var->type = UI_VAR_SPECIAL;
-    }
-    return ui_checkbox_var(obj, label, var);
+UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) {
+    return togglebutton_create(obj, gtk_check_button_new(), args);
 }
 
-UIWIDGET ui_checkbox_nv(UiObject *obj, char *label, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_INTEGER);
-    return ui_checkbox_var(obj, label, var);
+UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) {
+#ifdef UI_GTK3
+    return NULL; // TODO
+#else
+    return ui_checkbox_create(obj, args);
+#endif
 }
 
 
-UIWIDGET ui_radiobutton_var(UiObject *obj, char *label, UiVar *var) {
+
+
+
+
+UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
     GSList *rg = NULL;
     UiInteger *rgroup;
     
+    UiVar* var = NULL;
+    if (args.value) {
+        var = uic_create_value_var(current->ctx, args.value);
+    } else if (args.varname) {
+        var = uic_create_var(obj->ctx, args.varname, UI_VAR_INTEGER);
+    }
+    
+    UiBool first = FALSE;
     if(var) {
         rgroup = var->value;
         rg = rgroup->obj;
+        if(!rg) {
+            first = TRUE;
+        }
     }
     
-    GtkWidget *rbutton = gtk_radio_button_new_with_label(rg, label);
+    GtkWidget *rbutton = gtk_radio_button_new_with_label(rg, args.label ? args.label : "");
     rg = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton));
     
     if(rgroup) {
@@ -171,17 +221,21 @@
         event->obj = obj;
         event->var = var;
         event->observers = NULL;
+        event->callback = NULL;
+        event->userdata = NULL;
         
         g_signal_connect(
                 rbutton,
                 "clicked",
                 G_CALLBACK(ui_radio_obs),
                 event);
-        g_signal_connect(
+        if(first) {
+            g_signal_connect(
                 rbutton,
                 "destroy",
                 G_CALLBACK(ui_destroy_vardata),
                 event);
+        }
     }
     
     UiContainer *ct = uic_get_current_container(obj);
@@ -190,21 +244,6 @@
     return rbutton;
 }
 
-UIWIDGET ui_radiobutton_deprecated(UiObject *obj, char *label, UiInteger *rgroup) {
-    UiVar *var = NULL;
-    if(rgroup) {
-        var = malloc(sizeof(UiVar));
-        var->value = rgroup;
-        var->type = UI_VAR_SPECIAL;
-    }
-    return ui_radiobutton_var(obj, label, var);
-}
-
-UIWIDGET ui_radiobutton_nv(UiObject *obj, char *label, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_INTEGER);
-    return ui_radiobutton_var(obj, label, var);
-}
-
 void ui_radio_obs(GtkToggleToolButton *widget, UiVarEventData *event) {
     UiInteger *i = event->var->value;
     
--- a/ui/gtk/button.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/button.h	Sun Jun 09 15:43:08 2024 +0200
@@ -37,6 +37,8 @@
 extern "C" {
 #endif
 
+void ui_button_set_icon_name(GtkWidget *button, const char *icon_name);
+    
 // event wrapper
 void ui_button_clicked(GtkWidget *widget, UiEventData *event);
 
--- a/ui/gtk/container.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/container.c	Sun Jun 09 15:43:08 2024 +0200
@@ -141,10 +141,11 @@
         gtk_widget_set_vexpand(widget, TRUE);
     }
     
-    int gwidth = ct->layout.gridwidth > 0 ? ct->layout.gridwidth : 1;
+    int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1;
+    int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1;
     
-    gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, gwidth, 1);
-    grid->x += gwidth;
+    gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan);
+    grid->x += colspan;
     
     ui_reset_layout(ct->layout);
     ct->current = widget;
@@ -171,6 +172,10 @@
     GtkAttachOptions xoptions = hexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL;
     GtkAttachOptions yoptions = vexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL;
     
+    int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1;
+    int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1;
+    // TODO: use colspan/rowspan
+    
     gtk_table_attach(GTK_TABLE(ct->widget), widget, grid->x, grid->x+1, grid->y, grid->y+1, xoptions, yoptions, 0, 0);
     grid->x++;
     int nw = grid->x > grid->width ? grid->x : grid->width;
@@ -254,12 +259,12 @@
     UiContainer *ct = current->container;
     UI_APPLY_LAYOUT1(current, args);
     
-    GtkWidget *vbox = ui_gtk_vbox_new(args.spacing);   
-    GtkWidget *widget = args.margin > 0 ? box_set_margin(vbox, args.margin) : vbox;
+    GtkWidget *box = type == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args.spacing) : ui_gtk_hbox_new(args.spacing);
+    GtkWidget *widget = args.margin > 0 ? box_set_margin(box, args.margin) : box;
     ct->add(ct, widget, TRUE);
     
-    UiObject *newobj = uic_object_new(obj, vbox);
-    newobj->container = ui_box_container(obj, vbox);
+    UiObject *newobj = uic_object_new(obj, box);
+    newobj->container = ui_box_container(obj, box);
     uic_obj_add(obj, newobj);
     
     return widget;
@@ -276,7 +281,8 @@
 
 
 UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) {
-    UiContainer *ct = uic_get_current_container(obj);
+    UiObject* current = uic_current_obj(obj);
+    UI_APPLY_LAYOUT1(current, args);
     GtkWidget *widget;
     
 #ifdef UI_GTK3
@@ -287,8 +293,8 @@
     gtk_widget_set_margin_start(grid, args.margin);
     gtk_widget_set_margin_end(grid, args.margin);
 #else
-    gtk_widget_set_margin_left(grid, margin);
-    gtk_widget_set_margin_right(grid, margin);
+    gtk_widget_set_margin_left(grid, args.margin);
+    gtk_widget_set_margin_right(grid, args.margin);
 #endif
     gtk_widget_set_margin_top(grid, args.margin);
     gtk_widget_set_margin_bottom(grid, args.margin);
@@ -309,7 +315,7 @@
         widget = grid;
     }
 #endif
-    ct->add(ct, widget, TRUE);
+    current->container->add(current->container, widget, TRUE);
     
     UiObject *newobj = uic_object_new(obj, grid);
     newobj->container = ui_grid_container(obj, grid);
@@ -319,6 +325,19 @@
 }
 
 
+UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    UI_APPLY_LAYOUT1(current, args);
+    
+    GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
+    UiObject *newobj = uic_object_new(obj, sw);
+    newobj->container = ui_scrolledwindow_container(obj, sw);
+    uic_obj_add(obj, newobj);
+    
+    return sw;
+}
+
+
 void ui_select_tab(UIWIDGET tabview, int tab) {
     gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab);
 }
@@ -367,11 +386,6 @@
     ct->layout.vexpand = expand;
 }
 
-void ui_layout_gridwidth(UiObject *obj, int width) {
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->layout.gridwidth = width;
-}
-
 void ui_layout_colspan(UiObject* obj, int cols) {
     UiContainer* ct = uic_get_current_container(obj);
     ct->layout.colspan = cols;
--- a/ui/gtk/container.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/container.h	Sun Jun 09 15:43:08 2024 +0200
@@ -61,7 +61,6 @@
     UiBool       hexpand;
     UiBool       vexpand;
     int          width;
-    int          gridwidth;
     int          colspan;
     int          rowspan;
 };
--- a/ui/gtk/display.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/display.c	Sun Jun 09 15:43:08 2024 +0200
@@ -33,6 +33,7 @@
 #include "container.h"
 #include "../common/context.h"
 #include "../common/object.h"
+#include "../ui/display.h"
 
 static void set_alignment(GtkWidget *widget, float xalign, float yalign) {
 #if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16
@@ -43,30 +44,60 @@
 #endif
 }
 
-UIWIDGET ui_label(UiObject *obj, char *label) { 
-    GtkWidget *widget = gtk_label_new(label);
+UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
+    GtkWidget *widget = gtk_label_new(args.label);
+    switch(args.align) {
+        case UI_ALIGN_DEFAULT: break;
+        case UI_ALIGN_LEFT: set_alignment(widget, 0, .5); break;
+        case UI_ALIGN_RIGHT: set_alignment(widget, 1, .5); break;
+        case UI_ALIGN_CENTER: break; // TODO
+    }
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, widget, FALSE);
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING);
+    if(var) {
+        UiString* value = (UiString*)var->value;
+        value->obj = widget;
+        value->get = ui_label_get;
+        value->set = ui_label_set;
+    }
+    
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, widget, FALSE);
     
     return widget;
 }
 
-UIWIDGET ui_llabel(UiObject *obj, char *label) {
-    UIWIDGET widget = ui_label(obj, label);
-    set_alignment(widget, 0, .5);
-    return widget;
+UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs args) {
+    args.align = UI_ALIGN_LEFT;
+    return ui_label_create(obj, args);
+}
+
+UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs args) {
+    args.align = UI_ALIGN_RIGHT;
+    return ui_label_create(obj, args);
 }
 
-UIWIDGET ui_rlabel(UiObject *obj, char *label) {
-    UIWIDGET widget = ui_label(obj, label);
-    //gtk_label_set_justify(GTK_LABEL(widget), GTK_JUSTIFY_RIGHT);
-    
-    set_alignment(widget, 1, .5);
-    return widget;
+char* ui_label_get(UiString *s) {
+    if(s->value.ptr) {
+        s->value.free(s->value.ptr);
+    }
+    s->value.ptr = g_strdup(gtk_label_get_text(GTK_LABEL(s->obj)));
+    s->value.free = (ui_freefunc)g_free;
+    return s->value.ptr;
 }
 
-UIWIDGET ui_space(UiObject *obj) {
+void ui_label_set(UiString *s, const char *value) {
+    gtk_label_set_text(GTK_LABEL(s->obj), value);
+    if(s->value.ptr) {
+        s->value.free(s->value.ptr);
+        s->value.ptr = NULL;
+        s->value.free = NULL;
+    }
+}
+
+UIWIDGET ui_space_deprecated(UiObject *obj) {
     GtkWidget *widget = gtk_label_new("");
     UiContainer *ct = uic_get_current_container(obj);
     ct->add(ct, widget, TRUE);
@@ -74,7 +105,7 @@
     return widget;
 }
 
-UIWIDGET ui_separator(UiObject *obj) {
+UIWIDGET ui_separator_deprecated(UiObject *obj) {
 #if UI_GTK3
     GtkWidget *widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
 #else
@@ -88,20 +119,12 @@
 
 /* ------------------------- progress bar ------------------------- */
 
-UIWIDGET ui_progressbar(UiObject *obj, UiDouble *value) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = value;
-    var->type = UI_VAR_SPECIAL;
-    return ui_progressbar_var(obj, var);
-}
-
-UIWIDGET ui_progressbar_nv(UiObject *obj, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_DOUBLE);
-    return ui_progressbar_var(obj, var);
-}
-
-UIWIDGET ui_progressbar_var(UiObject *obj, UiVar *var) {
+UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
     GtkWidget *progressbar = gtk_progress_bar_new();
+    
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_DOUBLE);
     if(var && var->value) {
         UiDouble *value = var->value;
         value->get = ui_progressbar_get;
@@ -110,8 +133,8 @@
         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar), 0.5);
     }
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, progressbar, FALSE);
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, progressbar, FALSE);
     
     return progressbar;
 }
@@ -125,3 +148,42 @@
     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(d->obj), value);
     d->value = value;
 }
+
+
+/* ------------------------- progress spinner ------------------------- */
+
+UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
+    GtkWidget *spinner = gtk_spinner_new();
+    
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER);
+    if(var && var->value) {
+        UiInteger *value = var->value;
+        value->get = ui_spinner_get;
+        value->set = ui_spinner_set;
+        value->obj = spinner;
+        ui_spinner_set(value, value->value);
+    }
+    
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, spinner, FALSE);
+    
+    return spinner;
+}
+
+int64_t ui_spinner_get(UiInteger *i) {
+    return i->value;
+}
+
+void ui_spinner_set(UiInteger *i, int64_t value) {
+    i->value = value;
+    if(i->obj) {
+        GtkSpinner *spinner = GTK_SPINNER(i->obj);
+        if(value != 0) {
+            gtk_spinner_start(spinner);
+        } else {
+            gtk_spinner_stop(spinner);
+        }
+    }
+}
--- a/ui/gtk/display.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/display.h	Sun Jun 09 15:43:08 2024 +0200
@@ -36,9 +36,14 @@
 extern "C" {
 #endif
 
+char* ui_label_get(UiString *s);
+void ui_label_set(UiString *s, const char *value);
+    
 UIWIDGET ui_progressbar_var(UiObject *obj, UiVar *var);
 double ui_progressbar_get(UiDouble *d);
 void ui_progressbar_set(UiDouble *d, double value);
+int64_t ui_spinner_get(UiInteger *i);
+void ui_spinner_set(UiInteger *i, int64_t value);
 
 #ifdef	__cplusplus
 }
--- a/ui/gtk/dnd.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/dnd.c	Sun Jun 09 15:43:08 2024 +0200
@@ -101,3 +101,19 @@
     return NULL;
 }
 */
+
+void ui_selection_settext(UiDnD *sel, char *str, int len) {
+    
+}
+
+void ui_selection_seturis(UiDnD *sel, char **uris, int nelm) {
+    
+}
+
+char* ui_selection_gettext(UiDnD *sel) {
+    return NULL;
+}
+
+UiFileList ui_selection_geturis(UiDnD *sel) {
+    return (UiFileList){NULL,0};
+}
--- a/ui/gtk/entry.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/entry.c	Sun Jun 09 15:43:08 2024 +0200
@@ -35,65 +35,46 @@
 #include "entry.h"
 
 
-UIWIDGET ui_spinner(UiObject *obj, int step, UiInteger *i) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = i;
-    var->type = UI_VAR_SPECIAL;
-    return ui_spinner_var(obj, step, 0, var, UI_VAR_INTEGER);
-}
-
-UIWIDGET ui_spinnerf(UiObject *obj, double step, int digits, UiDouble *d) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = d;
-    var->type = UI_VAR_SPECIAL;
-    return ui_spinner_var(obj, step, digits, var, UI_VAR_DOUBLE);
-}
-
-UIWIDGET ui_spinnerr(UiObject *obj, UiRange *r) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = r;
-    var->type = UI_VAR_SPECIAL;
-    return ui_spinner_var(obj, r->extent, 1, var, UI_VAR_RANGE);
-}
-
-UIWIDGET ui_spinner_nv(UiObject *obj, int step, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_INTEGER);
-    return ui_spinner_var(obj, step, 0, var, UI_VAR_INTEGER);
-}
-
-UIWIDGET ui_spinnerf_nv(UiObject *obj, double step, int digits, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_DOUBLE);
-    return ui_spinner_var(obj, step, digits, var, UI_VAR_DOUBLE);
-}
-
-UIWIDGET ui_spinnerr_nv(UiObject *obj, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_RANGE);
-    UiRange *r = var->value;
-    return ui_spinner_var(obj, r->extent, 1, var, UI_VAR_RANGE);
-}
-
-UIWIDGET ui_spinner_var(UiObject *obj, double step, int digits, UiVar *var, UiVarType type) {
+UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs args) {
     double min = 0;
     double max = 1000;
-    if(type == UI_VAR_RANGE) {
+    
+    UiObject* current = uic_current_obj(obj);
+    
+    UiVar *var = NULL;
+    if(args.varname) {
+        var = uic_get_var(obj->ctx, args.varname);
+    }
+    
+    if(!var) {
+        if(args.intvalue) {
+            var = uic_widget_var(obj->ctx, current->ctx, args.intvalue, NULL, UI_VAR_INTEGER);
+        } else if(args.doublevalue) {
+            var = uic_widget_var(obj->ctx, current->ctx, args.doublevalue, NULL, UI_VAR_DOUBLE);
+        } else if(args.rangevalue) {
+            var = uic_widget_var(obj->ctx, current->ctx, args.rangevalue, NULL, UI_VAR_RANGE);
+        }
+    }
+    
+    if(var && var->type == UI_VAR_RANGE) {
         UiRange *r = var->value;
         min = r->min;
         max = r->max;
     }
-    if(step == 0) {
-        step = 1;
+    if(args.step == 0) {
+        args.step = 1;
     }
 #ifdef UI_GTK2LEGACY
     if(min == max) {
         max = min + 1;
     }
 #endif
-    GtkWidget *spin = gtk_spin_button_new_with_range(min, max, step);
-    gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), digits);
+    GtkWidget *spin = gtk_spin_button_new_with_range(min, max, args.step);
+    gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), args.digits);
+    UiObserver **obs = NULL;
     if(var) {
         double value = 0;
-        UiObserver **obs = NULL;
-        switch(type) {
+        switch(var->type) {
             default: break;
             case UI_VAR_INTEGER: {
                 UiInteger *i = var->value;
@@ -126,26 +107,28 @@
             }
         }
         gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value);
-        
-        UiVarEventData *event = malloc(sizeof(UiVarEventData));
-        event->obj = obj;
-        event->var = var;
-        event->observers = obs;
-        
-        g_signal_connect(
-                spin,
-                "value-changed",
-                G_CALLBACK(ui_spinner_changed),
-                event);
-        g_signal_connect(
-                spin,
-                "destroy",
-                G_CALLBACK(ui_destroy_vardata),
-                event);
     }
     
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, spin, FALSE);
+    UiVarEventData *event = malloc(sizeof(UiVarEventData));
+    event->obj = obj;
+    event->var = var;
+    event->observers = obs;
+    event->callback = args.onchange;
+    event->userdata = args.onchangedata;
+
+    g_signal_connect(
+            spin,
+            "value-changed",
+            G_CALLBACK(ui_spinner_changed),
+            event);
+    g_signal_connect(
+            spin,
+            "destroy",
+            G_CALLBACK(ui_destroy_vardata),
+            event);
+    
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, spin, FALSE);
     
     return spin;
 }
@@ -160,15 +143,22 @@
 
 
 void ui_spinner_changed(GtkSpinButton *spinner, UiVarEventData *event) {
+    gdouble value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinner));
     UiEvent e;
     e.obj = event->obj;
     e.window = event->obj->window;
     e.document = event->obj->ctx->document;
-    e.eventdata = event->var->value;
-    e.intval = 0;
+    e.eventdata = &value;
+    e.intval = (int64_t)value;
     
-    UiObserver *observer = *event->observers;
-    ui_notify_evt(observer, &e);
+    if(event->callback) {
+        event->callback(&e, event->userdata);
+    }
+    
+    if(event->observers) {
+        UiObserver *observer = *event->observers;
+        ui_notify_evt(observer, &e);
+    }
 }
 
 
--- a/ui/gtk/entry.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/entry.h	Sun Jun 09 15:43:08 2024 +0200
@@ -22,7 +22,6 @@
 extern "C" {
 #endif
 
-UIWIDGET ui_spinner_var(UiObject *obj, double step, int digits, UiVar *var, UiVarType type);
 void ui_spinner_changed(GtkSpinButton *spinner, UiVarEventData *event);
 
 int64_t ui_spinbutton_getint(UiInteger *i);
--- a/ui/gtk/graphics.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/graphics.c	Sun Jun 09 15:43:08 2024 +0200
@@ -104,6 +104,8 @@
         event->obj = obj;
         event->callback = f;
         event->userdata = u;
+        event->customdata = NULL;
+        event->value = 0;
         
         g_signal_connect(G_OBJECT(widget),
                 "button-press-event",
--- a/ui/gtk/image.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/image.c	Sun Jun 09 15:43:08 2024 +0200
@@ -59,7 +59,7 @@
     }
 }
 
-// **** new functions ****
+// **** deprecated2****
 
 static UiIcon* get_icon(const char *name, int size, int scale) {
 #ifdef UI_SUPPORTS_SCALE
@@ -70,24 +70,58 @@
     if(info) {
         UiIcon *icon = malloc(sizeof(UiIcon));
         icon->info = info;
+        icon->pixbuf = NULL;
         return icon;
     }
     return NULL;
 }
 
-/*
-UiIcon* ui_icon(const char *name, int size) {
+UiIcon* ui_icon(const char* name, size_t size) {
     return get_icon(name, size, ui_get_scalefactor());
 }
-*/
+
+UiIcon* ui_imageicon(const char* file) {
+    GError *error = NULL;
+    GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(file, &error);
+    if(!pixbuf) {
+        fprintf(stderr, "UiError: Cannot load image: %s\n", file);
+        return NULL;
+    }
+    
+    UiIcon *icon = malloc(sizeof(UiIcon));
+    icon->info = NULL;
+    icon->pixbuf = pixbuf;
+    return icon;
+}
+
+void ui_icon_free(UiIcon* icon) {
+    if(icon->info) {
+        g_object_unref(icon->info);
+    }
+    if(icon->pixbuf) {
+        g_object_unref(icon->pixbuf);
+    }
+    free(icon);
+}
+
+UiIcon* ui_foldericon(size_t size) {
+    return ui_icon("folder", size);
+}
+
+UiIcon* ui_fileicon(size_t size) {
+    return ui_icon("application-x-generic", size);
+}
 
 UiIcon* ui_icon_unscaled(const char *name, int size) {
     return get_icon(name, size, 1);
 }
 
-void ui_free_icon(UiIcon *icon) {
-    g_object_unref(icon->info);
-    free(icon);
+GdkPixbuf* ui_icon_pixbuf(UiIcon *icon) {
+    if(!icon->pixbuf) {
+        GError *error = NULL;
+        icon->pixbuf = gtk_icon_info_load_icon(icon->info, &error);
+    }
+    return icon->pixbuf;
 }
 
 UiImage* ui_icon_image(UiIcon *icon) {
--- a/ui/gtk/image.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/image.h	Sun Jun 09 15:43:08 2024 +0200
@@ -42,6 +42,7 @@
     
 struct UiIcon {
     GtkIconInfo *info;
+    GdkPixbuf *pixbuf;
 };
 
 struct UiImage {
@@ -52,6 +53,7 @@
 
 GdkPixbuf* ui_get_image(const char *name);
 
+GdkPixbuf* ui_icon_pixbuf(UiIcon *icon);
 
 #ifdef	__cplusplus
 }
--- a/ui/gtk/menu.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/menu.c	Sun Jun 09 15:43:08 2024 +0200
@@ -74,6 +74,16 @@
     return mb;
 }
 
+void ui_add_menu_items(GtkWidget *parent, int i, UiMenu *menu, UiObject *obj) {
+    UiMenuItemI *it = menu->items_begin;
+    int index = 0;
+    while(it) {
+        createMenuItem[it->type](parent, index, it, obj);
+        it = it->next;
+        index++;
+    }
+}
+
 void add_menu_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj) {
     UiMenu *menu = (UiMenu*)item;
     
@@ -81,14 +91,8 @@
     GtkWidget *menu_item = gtk_menu_item_new_with_mnemonic(menu->label);
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu_widget);
     
-    UiMenuItemI *it = menu->items_begin;
-    int index = 0;
-    while(it) {
-        createMenuItem[it->type](menu_widget, index, it, obj);
-        
-        it = it->next;
-        index++;
-    }
+    ui_add_menu_items(menu_widget, i, menu, obj);
+    
     
     gtk_menu_shell_append(GTK_MENU_SHELL(parent), menu_item);
 }
@@ -105,6 +109,7 @@
         event->userdata = i->userdata;
         event->callback = i->callback;
         event->value = 0;
+        event->customdata = NULL;
 
         g_signal_connect(
                 widget,
@@ -121,7 +126,10 @@
     gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
     
     if(i->groups) {
-        uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, i->groups);
+        CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups);
+        cxListAddArray(groups, i->groups, i->ngroups);
+        uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups);
+        cxListDestroy(groups);
     }
 }
 
@@ -185,7 +193,8 @@
         event->userdata = ci->userdata;
         event->callback = ci->callback;
         event->value = 0;
-
+        event->customdata = NULL;
+        
         g_signal_connect(
                 widget,
                 "toggled",
@@ -235,8 +244,8 @@
     ls->index = index;
     ls->oldcount = 0;
     
-    // TODO:
-    //ls->list = il->list;
+    UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    ls->list = var->value;
     
     ls->callback = il->callback;
     ls->userdata = il->userdata;
@@ -284,6 +293,7 @@
             event->userdata = list->userdata;
             event->callback = list->callback;
             event->value = i - 1;
+            event->customdata = NULL;
 
             g_signal_connect(
                 widget,
@@ -408,6 +418,7 @@
         event->userdata = userdata;
         event->callback = f;
         event->value = 0;
+        event->customdata = NULL;
 
         g_signal_connect(
                 widget,
@@ -462,6 +473,7 @@
         event->userdata = userdata;
         event->callback = f;
         event->value = 0;
+        event->customdata = NULL;
 
         g_signal_connect(
                 widget,
--- a/ui/gtk/menu.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/menu.h	Sun Jun 09 15:43:08 2024 +0200
@@ -55,6 +55,8 @@
 
 GtkWidget *ui_create_menubar(UiObject *obj);
 
+void ui_add_menu_items(GtkWidget *parent, int i, UiMenu *menu, UiObject *obj);
+
 void add_menu_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj);
 void add_menuitem_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj);
 void add_menuitem_st_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj);
--- a/ui/gtk/objs.mk	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/objs.mk	Sun Jun 09 15:43:08 2024 +0200
@@ -38,7 +38,6 @@
 GTKOBJ += button.o
 GTKOBJ += display.o
 GTKOBJ += text.o
-GTKOBJ += model.o
 GTKOBJ += tree.o
 GTKOBJ += image.o
 GTKOBJ += graphics.o
--- a/ui/gtk/range.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/range.c	Sun Jun 09 15:43:08 2024 +0200
@@ -61,6 +61,7 @@
         event->userdata = userdata;
         event->callback = f;
         event->value = 0;
+        event->customdata = NULL;
         
         g_signal_connect(
                 G_OBJECT(scrollbar),
--- a/ui/gtk/text.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/text.c	Sun Jun 09 15:43:08 2024 +0200
@@ -33,6 +33,10 @@
 #include "text.h"
 #include "container.h"
 
+#include <cx/printf.h>
+
+#include <gdk/gdkkeysyms.h>
+
 
 #include "../common/types.h"
 
@@ -534,9 +538,14 @@
 }
 
 
-static UIWIDGET create_textfield_var(UiObject *obj, int width, UiBool frameless, UiBool password, UiVar *var) {
+
+
+static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool password, UiTextFieldArgs args) {
     GtkWidget *textfield = gtk_entry_new();
     
+    UiObject* current = uic_current_obj(obj);
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING);
+    
     UiTextField *uitext = malloc(sizeof(UiTextField));
     uitext->ctx = obj->ctx;
     uitext->var = var;
@@ -547,8 +556,8 @@
                 G_CALLBACK(ui_textfield_destroy),
                 uitext);
     
-    if(width > 0) {
-        gtk_entry_set_width_chars(GTK_ENTRY(textfield), width);
+    if(args.width > 0) {
+        gtk_entry_set_width_chars(GTK_ENTRY(textfield), args.width);
     }
     if(frameless) {
         // TODO: gtk2legacy workaroud
@@ -586,40 +595,25 @@
     return textfield;
 }
 
-static UIWIDGET create_textfield_nv(UiObject *obj, int width, UiBool frameless, UiBool password, char *varname) {
-    UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_STRING);
-    if(var) {
-        return create_textfield_var(obj, width, frameless, password, var);
-    } else {
-        // TODO: error
-    }
-    return NULL;
+UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs args) {
+    return create_textfield(obj, FALSE, FALSE, args);
 }
 
-static UIWIDGET create_textfield(UiObject *obj, int width, UiBool frameless, UiBool password, UiString *value) {
-    UiVar *var = NULL;
-    if(value) {
-        var = malloc(sizeof(UiVar));
-        var->value = value;
-        var->type = UI_VAR_SPECIAL;
-        var->from = NULL;
-        var->from_ctx = NULL;
-    }
-    return create_textfield_var(obj, width, frameless, password, var);
+UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs args) {
+    return create_textfield(obj, TRUE, FALSE, args);
 }
 
+UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs args) {
+    return create_textfield(obj, FALSE, TRUE, args);
+}
+
+
 void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield) {
-    if(textfield->var) {
-        UiText *text = textfield->var->value;
-        if(text->undomgr) {
-            ui_destroy_undomgr(text->undomgr);
-        }
-        ui_destroy_boundvar(textfield->ctx, textfield->var);
-    }
     free(textfield);
 }
 
 void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) {
+    // changed event is only registered, if the textfield->var != NULL
     UiString *value = textfield->var->value;
     if(value->observers) {
         UiEvent e;
@@ -632,45 +626,6 @@
     }
 }
 
-UIWIDGET ui_textfield_deprecated(UiObject *obj, UiString *value) {
-    return create_textfield(obj, 0, FALSE, FALSE, value);
-}
-
-UIWIDGET ui_textfield_nv(UiObject *obj, char *varname) {
-    return create_textfield_nv(obj, 0, FALSE, FALSE, varname);
-}
-
-UIWIDGET ui_textfield_w(UiObject *obj, int width, UiString *value) {
-    return create_textfield(obj, width, FALSE, FALSE, value);
-}
-
-UIWIDGET ui_textfield_wnv(UiObject *obj, int width, char *varname) {
-    return create_textfield_nv(obj, width, FALSE, FALSE, varname);
-}
-
-UIWIDGET ui_frameless_textfield_deprecated(UiObject *obj, UiString *value) {
-    return create_textfield(obj, 0, TRUE, FALSE, value);
-}
-
-UIWIDGET ui_frameless_textfield_nv(UiObject *obj, char *varname) {
-    return create_textfield_nv(obj, 0, TRUE, FALSE, varname);
-}
-
-UIWIDGET ui_passwordfield_deprecated(UiObject *obj, UiString *value) {
-    return create_textfield(obj, 0, FALSE, TRUE, value);
-}
-
-UIWIDGET ui_passwordfield_nv(UiObject *obj, char *varname) {
-    return create_textfield_nv(obj, 0, FALSE, TRUE, varname);
-}
-
-UIWIDGET ui_passwordfield_w(UiObject *obj, int width, UiString *value) {
-    return create_textfield(obj, width, FALSE, TRUE, value);
-}
-
-UIWIDGET ui_passwordfield_wnv(UiObject *obj, int width, char *varname) {
-    return create_textfield_nv(obj, width, FALSE, TRUE, varname);
-}
 
 char* ui_textfield_get(UiString *str) {
     if(str->value.ptr) {
@@ -689,3 +644,283 @@
         str->value.free = NULL;
     }
 }
+
+// ----------------------- path textfield -----------------------
+
+// TODO: move to common
+static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) {
+    cxstring *pathelms;
+    size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms);
+
+    if (nelm == 0) {
+        *ret_nelm = 0;
+        return NULL;
+    }
+
+    UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm));
+    size_t n = nelm;
+    int j = 0;
+    for (int i = 0; i < nelm; i++) {
+        cxstring c = pathelms[i];
+        if (c.length == 0) {
+            if (i == 0) {
+                c.length = 1;
+            }
+            else {
+                n--;
+                continue;
+            }
+        }
+
+        cxmutstr m = cx_strdup(c);
+        elms[j].name = m.ptr;
+        elms[j].name_len = m.length;
+        
+        size_t elm_path_len = c.ptr + c.length - full_path;
+        cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len));
+        elms[j].path = elm_path.ptr;
+        elms[j].path_len = elm_path.length;
+
+        j++;
+    }
+    *ret_nelm = n;
+
+    return elms;
+}
+
+static gboolean path_textfield_btn_pressed(GtkWidget *widget, GdkEventButton *event, UiPathTextField *pathtf) {
+    gtk_box_pack_start(GTK_BOX(pathtf->hbox), pathtf->entry, TRUE, TRUE, 0);
+    gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox);
+    
+    gtk_widget_show(pathtf->entry);
+    gtk_widget_grab_focus(pathtf->entry);    
+    
+    return TRUE;
+}
+
+static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) {
+    for(int i=0;i<nelm;i++) {
+        free(elms[i].name);
+        free(elms[i].path);
+    }
+    free(elms);
+}
+
+static void ui_path_textfield_destroy(GtkWidget *object, UiPathTextField *pathtf) {
+    free(pathtf->current_path);
+    g_object_unref(pathtf->entry);
+    free(pathtf);
+}
+
+static void ui_path_textfield_activate(GtkWidget *entry, UiPathTextField *pathtf) {
+    const gchar *text = gtk_entry_get_text(GTK_ENTRY(pathtf->entry));
+    if(strlen(text) == 0) {
+        return;
+    }
+    
+    UiObject *obj = pathtf->obj;
+    
+    if(ui_pathtextfield_update(pathtf, text)) {
+        return;
+    }
+    
+    if(pathtf->onactivate) {
+        UiEvent evt;
+        evt.obj = obj;
+        evt.window = obj->window;
+        evt.document = obj->ctx->document;
+        evt.eventdata = (char*)text;
+        evt.intval = -1;
+        pathtf->onactivate(&evt, pathtf->onactivatedata);
+    }
+}
+
+static gboolean ui_path_textfield_key_press(GtkWidget *self, GdkEventKey *event, UiPathTextField *pathtf) {
+    if (event->keyval == GDK_KEY_Escape) {
+        // reset GtkEntry value
+        gtk_entry_set_text(GTK_ENTRY(self), pathtf->current_path);
+        const gchar *text = gtk_entry_get_text(GTK_ENTRY(self));
+        ui_pathtextfield_update(pathtf, text);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static GtkWidget* create_path_button_box() {
+    GtkWidget *bb = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bb), GTK_BUTTONBOX_EXPAND); // linked style
+    gtk_box_set_homogeneous(GTK_BOX(bb), FALSE);
+    gtk_box_set_spacing(GTK_BOX(bb), 0);
+    return bb;
+}
+
+UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
+    UiPathTextField *pathtf = malloc(sizeof(UiPathTextField));
+    memset(pathtf, 0, sizeof(UiPathTextField));
+    pathtf->obj = obj;
+    pathtf->getpathelm = args.getpathelm;
+    pathtf->getpathelmdata = args.getpathelmdata;
+    pathtf->onactivate = args.onactivate;
+    pathtf->onactivatedata = args.onactivatedata;
+    pathtf->ondragcomplete = args.ondragcomplete;
+    pathtf->ondragcompletedata = args.ondragcompletedata;
+    pathtf->ondragstart = args.ondragstart;
+    pathtf->ondragstartdata = args.ondragstartdata;
+    pathtf->ondrop = args.ondrop;
+    pathtf->ondropdata = args.ondropsdata;
+    
+    if(!pathtf->getpathelm) {
+        pathtf->getpathelm = default_pathelm_func;
+        pathtf->getpathelmdata = NULL;
+    }
+    
+    // top level container for the path textfield is a GtkEventBox
+    // the event box is needed to handle background button presses
+    GtkWidget *eventbox = gtk_event_box_new();
+    g_signal_connect(
+            eventbox,
+            "button-press-event",
+            G_CALLBACK(path_textfield_btn_pressed),
+            pathtf);
+    g_signal_connect(
+            eventbox,
+            "destroy",
+            G_CALLBACK(ui_path_textfield_destroy),
+            pathtf);
+    
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, eventbox, FALSE);
+    
+    // hbox as parent for the GtkEntry and GtkButtonBox
+    GtkWidget *hbox = ui_gtk_hbox_new(0);
+    pathtf->hbox = hbox;
+    gtk_container_add(GTK_CONTAINER(eventbox), hbox);
+    gtk_widget_set_name(hbox, "path-textfield-box");
+    
+    // create GtkEntry, that is also visible by default (with input yet)
+    pathtf->entry = gtk_entry_new();
+    g_object_ref(G_OBJECT(pathtf->entry));
+    gtk_box_pack_start(GTK_BOX(hbox), pathtf->entry, TRUE, TRUE, 0);
+    
+    g_signal_connect(
+            pathtf->entry,
+            "activate",
+            G_CALLBACK(ui_path_textfield_activate),
+            pathtf);
+    g_signal_connect(
+            pathtf->entry,
+            "key-press-event",
+            G_CALLBACK(ui_path_textfield_key_press),
+            pathtf);
+    
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING);
+    if (var) {
+        UiString* value = (UiString*)var->value;
+        value->obj = pathtf;
+        value->get = ui_path_textfield_get;
+        value->set = ui_path_textfield_set;
+        
+        if(value->value.ptr) {
+            char *str = strdup(value->value.ptr);
+            ui_string_set(value, str);
+            free(str);
+        }
+    }
+    
+    return hbox;
+}
+
+void ui_path_button_clicked(GtkWidget *widget, UiEventData *event) {
+    UiPathElm *elm = event->customdata;
+    cxmutstr path = cx_strdup(cx_strn(elm->path, elm->path_len));
+    UiEvent evt;
+    evt.obj = event->obj;
+    evt.window = evt.obj->window;
+    evt.document = evt.obj->ctx->document;
+    evt.eventdata = elm->path;
+    evt.intval = event->value;
+    event->callback(&evt, event->userdata);
+    free(path.ptr);
+}
+
+int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path) {
+    size_t full_path_len = strlen(full_path);
+    
+    size_t nelm = 0;
+    UiPathElm* path_elm = pathtf->getpathelm(full_path, full_path_len, &nelm, pathtf->getpathelmdata);
+    if (!path_elm) {
+        return 1;
+    }
+    
+    free(pathtf->current_path);
+    pathtf->current_path = strdup(full_path);
+    
+    ui_pathelm_destroy(pathtf->current_pathelms, pathtf->current_nelm);
+    pathtf->current_pathelms = path_elm;
+    pathtf->current_nelm = nelm;
+    
+    GtkWidget *buttonbox = create_path_button_box();
+    pathtf->buttonbox = buttonbox;
+    
+    // switch from entry to buttonbox
+    gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->entry);
+    gtk_box_pack_start(GTK_BOX(pathtf->hbox), buttonbox, FALSE, FALSE, 0);
+    
+    for (int i=0;i<nelm;i++) {
+        UiPathElm *elm = &path_elm[i];
+        
+        cxmutstr name = cx_strdup(cx_strn(elm->name, elm->name_len));
+        GtkWidget *button = gtk_button_new_with_label(name.ptr);
+        free(name.ptr);
+        
+        if(pathtf->onactivate) {
+            UiEventData *eventdata = malloc(sizeof(UiEventData));
+            eventdata->callback = pathtf->onactivate;
+            eventdata->userdata = pathtf->onactivatedata;
+            eventdata->obj = pathtf->obj;
+            eventdata->customdata = elm;
+            eventdata->value = i;
+            
+            g_signal_connect(
+                    button,
+                    "clicked",
+                    G_CALLBACK(ui_path_button_clicked),
+                    eventdata);
+            
+            g_signal_connect(
+                    button,
+                    "destroy",
+                    G_CALLBACK(ui_destroy_userdata),
+                    eventdata);
+        }
+        
+        gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 0);
+    }
+    
+    gtk_widget_show_all(buttonbox);
+    
+    return 0;
+}
+
+char* ui_path_textfield_get(UiString *str) {
+    if(str->value.ptr) {
+        str->value.free(str->value.ptr);
+    }
+    UiPathTextField *tf = str->obj;
+    str->value.ptr = g_strdup(gtk_entry_get_text(GTK_ENTRY(tf->entry)));
+    str->value.free = (ui_freefunc)g_free;
+    return str->value.ptr;
+}
+
+void ui_path_textfield_set(UiString *str, const char *value) {
+    UiPathTextField *tf = str->obj;
+    gtk_entry_set_text(GTK_ENTRY(tf->entry), value);
+    ui_pathtextfield_update(tf, value);
+    if(str->value.ptr) {
+        str->value.free(str->value.ptr);
+        str->value.ptr = NULL;
+        str->value.free = NULL;
+    }
+}
--- a/ui/gtk/text.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/text.h	Sun Jun 09 15:43:08 2024 +0200
@@ -72,6 +72,31 @@
     // TODO: validatefunc
 } UiTextField;
 
+typedef struct UiPathTextField {
+    UiObject *obj;
+    
+    GtkWidget *hbox;
+    GtkWidget *entry;
+    GtkWidget *buttonbox;
+    
+    char *current_path;
+    UiPathElm *current_pathelms;
+    size_t current_nelm;
+    
+    ui_pathelm_func getpathelm;
+    void* getpathelmdata;
+
+    ui_callback onactivate;
+    void* onactivatedata;
+    
+    ui_callback ondragstart;
+    void* ondragstartdata;
+    ui_callback ondragcomplete;
+    void* ondragcompletedata;
+    ui_callback ondrop;
+    void* ondropdata;
+} UiPathTextField;
+
 UIWIDGET ui_textarea_var(UiObject *obj, UiVar *var);
 void ui_textarea_destroy(GtkWidget *object, UiTextArea *textarea);
 
@@ -110,6 +135,10 @@
 char* ui_textfield_get(UiString *str);
 void ui_textfield_set(UiString *str, const char *value);
 
+int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path);
+char* ui_path_textfield_get(UiString *str);
+void ui_path_textfield_set(UiString *str, const char *value);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/ui/gtk/toolbar.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/toolbar.c	Sun Jun 09 15:43:08 2024 +0200
@@ -31,6 +31,7 @@
 #include <string.h>
 
 #include "toolbar.h"
+#include "menu.h"
 #include "button.h"
 #include "image.h"
 #include "tree.h"
@@ -40,192 +41,8 @@
 #include <cx/array_list.h>
 #include "../common/context.h"
 
-static CxMap *toolbar_items;
-static CxList *defaults;
-
-void ui_toolbar_init() {
-    toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
-    defaults = cxLinkedListCreateSimple(CX_STORE_POINTERS);
-}
-
-void ui_toolitem(char *name, char *label, ui_callback f, void *udata) {
-    ui_toolitem_img(name, label, NULL, f, udata);
-}
-
-void ui_toolitem_st(char *name, char *stockid, ui_callback f, void *userdata) {
-    ui_toolitem_stgr(name, stockid, f, userdata, -1);
-}
-
-void ui_toolitem_sti(char *name, char *stockid, ui_callback f, void *userdata) {
-    ui_toolitem_stgri(name, stockid, f, userdata, -1);
-}
-
-void ui_toolitem_stgr(char *name, char *stockid, ui_callback f, void *userdata, ...) {
-    va_list ap;
-    va_start(ap, userdata);
-    ui_toolitem_vstgr(name, stockid, 0, f, userdata, ap);
-    va_end(ap);
-}
-
-void ui_toolitem_stgri(char *name, char *stockid, ui_callback f, void *userdata, ...) {
-    va_list ap;
-    va_start(ap, userdata);
-    ui_toolitem_vstgr(name, stockid, 1, f, userdata, ap);
-    va_end(ap);
-}
-
-void ui_toolitem_img(char *name, char *label, char *img, ui_callback f, void *udata) {
-    UiToolItem *item = malloc(sizeof(UiToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_widget;
-    item->label = label;
-    item->image = img;
-    item->callback = f;
-    item->userdata = udata;
-    item->isimportant = 0;
-    item->groups = NULL;
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-void ui_toolitem_vstgr(
-        char *name,
-        char *stockid,
-        int isimportant,
-        ui_callback f,
-        void *userdata,
-        va_list ap)
-{
-    UiStToolItem *item = malloc(sizeof(UiStToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_st_widget;
-    item->stockid = stockid;
-    item->callback = f;
-    item->userdata = userdata;
-    item->groups = NULL;
-    item->isimportant = isimportant;
-    
-    // add groups
-    int group;
-    while((group = va_arg(ap, int)) != -1) {
-        if(!item->groups) {
-            item->groups = cxArrayListCreateSimple(sizeof(int), 16);
-        }
-        cxListAdd(item->groups, &group);
-    }
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-void ui_toolitem_toggle(const char *name, const char *label, const char *img, UiInteger *i) {
-    UiToggleToolItem *item = malloc(sizeof(UiToggleToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget;
-    item->label = label;
-    item->image = img;
-    item->stockid = NULL;
-    item->groups = NULL;
-    item->isimportant = 0;
-    item->value = i;
-    item->var = NULL;
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-void ui_toolitem_toggle_st(const char *name, const char *stockid, UiInteger *i) {
-    UiToggleToolItem *item = malloc(sizeof(UiToggleToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget;
-    item->label = NULL;
-    item->image = NULL;
-    item->stockid = stockid;
-    item->groups = NULL;
-    item->isimportant = 0;
-    item->value = i;
-    item->var = NULL;
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-void ui_toolitem_toggle_nv(const char *name, const char *label, const char *img, const char *intvar) {
-    UiToggleToolItem *item = malloc(sizeof(UiToggleToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget;
-    item->label = label;
-    item->image = img;
-    item->stockid = NULL;
-    item->groups = NULL;
-    item->isimportant = 0;
-    item->value = NULL;
-    item->var = intvar;
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-void ui_toolitem_toggle_stnv(const char *name, const char *stockid, const char *intvar) {
-    UiToggleToolItem *item = malloc(sizeof(UiToggleToolItem));
-    item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget;
-    item->label = NULL;
-    item->image = NULL;
-    item->stockid = stockid;
-    item->groups = NULL;
-    item->isimportant = 0;
-    item->value = NULL;
-    item->var = intvar;
-    
-    cxMapPut(toolbar_items, name, item);
-}
-
-
-void ui_toolbar_combobox(
-        char *name,
-        UiList *list,
-        ui_getvaluefunc getvalue,
-        ui_callback f,
-        void *udata)
-{
-    UiToolbarComboBox *cb = malloc(sizeof(UiToolbarComboBox));
-    cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox;
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = list;
-    var->type = UI_VAR_SPECIAL;
-    var->from = NULL;
-    var->from_ctx = NULL;
-    cb->var = var;
-    cb->getvalue = getvalue;
-    cb->callback = f;
-    cb->userdata = udata;
-    
-    cxMapPut(toolbar_items, name, cb);
-}
-
-void ui_toolbar_combobox_str(
-        char *name,
-        UiList *list,
-        ui_callback f,
-        void *udata)
-{
-    ui_toolbar_combobox(name, list, ui_strmodel_getvalue, f, udata);
-}
-
-void ui_toolbar_combobox_nv(
-        char *name,
-        char *listname,
-        ui_getvaluefunc getvalue,
-        ui_callback f,
-        void *udata)
-{
-    UiToolbarComboBoxNV *cb = malloc(sizeof(UiToolbarComboBoxNV));
-    cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox_nv;  
-    cb->listname = listname;
-    cb->getvalue = getvalue;
-    cb->callback = f;
-    cb->userdata = udata;
-    
-    cxMapPut(toolbar_items, name, cb);
-}
-
 
 GtkWidget* ui_create_toolbar(UiObject *obj) {
-    if(!defaults) {
-        return NULL;
-    }
-    
     GtkWidget *toolbar = gtk_toolbar_new();
 #ifdef UI_GTK3
     gtk_style_context_add_class(
@@ -233,6 +50,16 @@
             GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
 #endif
     
+    CxMap *items = uic_get_toolbar_items();
+    CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);
+    CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);
+    CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);
+    
+    ui_toolbar_add_items(obj, toolbar, items, left_defaults);
+    ui_toolbar_add_items(obj, toolbar, items, center_defaults);
+    ui_toolbar_add_items(obj, toolbar, items, right_defaults);
+    
+    /*
     GtkToolbar *tb = GTK_TOOLBAR(toolbar);
     CxIterator i = cxListIterator(defaults);
     cx_foreach(char *, def, i) {
@@ -245,57 +72,86 @@
             fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", def);
         }
     }
+    */
     
     return toolbar;
 }
 
-void add_toolitem_widget(GtkToolbar *tb, UiToolItem *item, UiObject *obj) {
-    GtkToolItem *button = gtk_tool_button_new(NULL, item->label);
-    gtk_tool_item_set_homogeneous(button, FALSE);
-    if(item->image) {
-        GdkPixbuf *pixbuf = ui_get_image(item->image);
-        GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf);
-        gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(button), image);
+static void create_item(UiObject *obj, GtkWidget *toolbar, UiToolbarItemI *i) {
+    GtkToolbar *tb = GTK_TOOLBAR(toolbar);
+    switch(i->type) {
+        case UI_TOOLBAR_ITEM: {
+            add_toolitem_widget(tb, (UiToolbarItem*)i, obj);
+            break;
+        }
+        case UI_TOOLBAR_TOGGLEITEM: {
+            add_toolitem_toggle_widget(tb, (UiToolbarToggleItem*)i, obj);
+            break;
+        }
+        case UI_TOOLBAR_MENU: {
+            add_toolitem_menu_widget(tb, (UiToolbarMenuItem*)i, obj);
+            break;
+        }
+        default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type);
+    }
+}
+
+void ui_toolbar_add_items(UiObject *obj, GtkWidget *toolbar, CxMap *items, CxList *defaults) {
+    // add pre-configured items
+    CxIterator i = cxListIterator(defaults);
+    cx_foreach(char*, def, i) {
+        UiToolbarItemI* item = uic_toolbar_get_item(def);
+        if (!item) {
+            fprintf(stderr, "unknown toolbar item: %s\n", def);
+            continue;
+        }
+        create_item(obj, toolbar, item);
+    }
+}
+
+static void set_toolbutton_icon(GtkToolItem *item, const char *icon_name) {
+#if GTK_MAJOR_VERSION >= 3
+        gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), icon_name);
+#else
+        UiIcon *icon = ui_icon(icon_name, 24);
+        if(icon) {
+            GdkPixbuf *pixbuf = ui_icon_pixbuf(icon);
+            if(pixbuf) {
+                GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf);
+                gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item), image);
+            }
+        }
+#endif
+}
+
+void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj) {
+    GtkToolItem *button;
+    if(item->args.stockid) {
+#ifdef UI_GTK2
+        button = gtk_tool_button_new_from_stock(item->args.stockid);
+#else
+        // TODO: gtk3 stock
+        button = gtk_tool_button_new(NULL, item->args.label);
+#endif
     } else {
-        gtk_tool_item_set_is_important(button, TRUE);
+        button = gtk_tool_button_new(NULL, item->args.label);
     }
     
-    if(item->callback) {
+    gtk_tool_item_set_homogeneous(button, FALSE);
+    if(item->args.icon) {
+        set_toolbutton_icon(button, item->args.icon);
+    }
+    gtk_tool_item_set_is_important(button, TRUE);
+    
+    if(item->args.onclick) {
         UiEventData *event = cxMalloc(
                 obj->ctx->allocator,
                 sizeof(UiEventData));
         event->obj = obj;
-        event->userdata = item->userdata;
-        event->callback = item->callback;
-        
-        g_signal_connect(
-                button,
-                "clicked",
-                G_CALLBACK(ui_button_clicked),
-                event);
-    }
-    
-    gtk_toolbar_insert(tb, button, -1);
-    
-    if(item->groups) {
-        uic_add_group_widget(obj->ctx, button, (ui_enablefunc)ui_set_enabled, item->groups);
-    }
-}
-
-void add_toolitem_st_widget(GtkToolbar *tb, UiStToolItem *item, UiObject *obj) {
-    GtkToolItem *button = gtk_tool_button_new_from_stock(item->stockid);
-    gtk_tool_item_set_homogeneous(button, FALSE);
-    if(item->isimportant) {
-        gtk_tool_item_set_is_important(button, TRUE);
-    }
-    
-    if(item->callback) {
-        UiEventData *event = cxMalloc(
-                obj->ctx->allocator,
-                sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = item->userdata;
-        event->callback = item->callback;
+        event->callback = item->args.onclick;
+        event->userdata = item->args.onclickdata;
+        event->customdata = NULL;
+        event->value = 0;
         
         g_signal_connect(
                 button,
@@ -306,74 +162,71 @@
     
     gtk_toolbar_insert(tb, button, -1);
     
+    /*
     if(item->groups) {
         uic_add_group_widget(obj->ctx, button, (ui_enablefunc)ui_set_enabled, item->groups);
     }
+    */
 }
 
-void add_toolitem_toggle_widget(GtkToolbar *tb, UiToggleToolItem *item, UiObject *obj) {
+void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObject *obj) {
     GtkToolItem *button;
-    if(item->stockid) {
-        button = gtk_toggle_tool_button_new_from_stock(item->stockid);
+    if(item->args.stockid) {
+#ifdef UI_GTK2
+        button = gtk_toggle_tool_button_new_from_stock(item->args.stockid);
+#else
+        button = gtk_toggle_tool_button_new_from_stock(item->args.stockid); // TODO: gtk3 stock
+#endif
     } else {
         button = gtk_toggle_tool_button_new();
         gtk_tool_item_set_homogeneous(button, FALSE);
-        if(item->label) {
-            gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->label);
+        if(item->args.label) {
+            gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->args.label);
         }
-        if(item->image) {
-            GdkPixbuf *pixbuf = ui_get_image(item->image);
-            GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf);
-            gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(button), image);
+        if(item->args.icon) {
+            set_toolbutton_icon(button, item->args.icon);
         }    
     }
     
-    UiVar *var;
-    if(item->value) {
-        var = malloc(sizeof(UiVar));
-        var->value = item->value;
-        var->type = UI_VAR_SPECIAL;
-        var->from = NULL;
-        var->from_ctx = NULL;
-    } else {
-        var = uic_create_var(obj->ctx, item->var, UI_VAR_INTEGER);
-    }
-    
-    if(var->value) {
-        UiInteger *i = var->value;
-        i->get = ui_tool_toggle_button_get;
-        i->set = ui_tool_toggle_button_set;
-        i->obj = button;
-        
-        if(i->value != 0) {
-            gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(button), TRUE);
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER);
+    if(var) {
+        UiInteger *i = (UiInteger*)var->value;
+	if(i) {
+            i->get = ui_tool_toggle_button_get;
+            i->set = ui_tool_toggle_button_set;
+            i->obj = button;
+
+            if(i->value != 0) {
+                gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(button), TRUE);
+            }
         }
     }
     
-    // register event
-    // the event func will call the UiInteger observer callbacks
-    UiEventData *event = cxMalloc(
-            obj->ctx->allocator,
-            sizeof(UiEventData));
+    UiVarEventData *event = cxMalloc(
+                obj->ctx->allocator,
+                sizeof(UiVarEventData));
     event->obj = obj;
-    event->userdata = var;
-    event->callback = NULL;
+    event->callback = item->args.onchange;
+    event->userdata = item->args.onchangedata;
+    event->var = var;
 
     g_signal_connect(
-            button,
-            "toggled",
-            G_CALLBACK(ui_tool_button_toggled),
-            event);
+        button,
+        "toggled",
+        G_CALLBACK(ui_tool_button_toggled),
+        event);
     
     // add item to toolbar
     gtk_toolbar_insert(tb, button, -1);
     
+    /*
     if(item->groups) {
         uic_add_group_widget(obj->ctx, button, (ui_enablefunc)ui_set_enabled, item->groups);
     }
+    */
 }
 
-void ui_tool_button_toggled(GtkToggleToolButton *widget, UiEventData *event) {
+void ui_tool_button_toggled(GtkToggleToolButton *widget, UiVarEventData *event) {
     UiEvent e;
     e.obj = event->obj;
     e.window = event->obj->window;
@@ -381,10 +234,16 @@
     e.eventdata = NULL;
     e.intval = gtk_toggle_tool_button_get_active(widget);
     
-    UiVar *var = event->userdata;
-    UiInteger *i = var->value;
+    if(event->callback) {
+        event->callback(&e, event->userdata);
+    }
     
-    ui_notify_evt(i->observers, &e);
+    UiVar *var = event->var;
+    UiInteger *i = var ? var->value : NULL;
+    
+    if(i) {
+        ui_notify_evt(i->observers, &e);
+    }
 }
 
 int64_t ui_tool_toggle_button_get(UiInteger *integer) {
@@ -398,6 +257,70 @@
     integer->value = s;
 }
 
+
+
+typedef struct UiToolbarMenuWidget {
+    GtkWidget *button;
+    GtkMenu *menu;
+} UiToolbarMenuWidget;
+
+static void ui_toolbar_menubutton_clicked(GtkWidget *widget, UiToolbarMenuWidget *menu) {
+    int x;
+    gtk_menu_popup_at_widget(menu->menu, menu->button, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
+}
+
+static void ui_toolbar_menubutton_destroy(GtkWidget *widget, UiToolbarMenuWidget *menu) {
+    free(menu);
+}
+
+void add_toolitem_menu_widget(GtkToolbar *tb, UiToolbarMenuItem *item, UiObject *obj) {
+    GtkToolItem *button;
+    if(item->args.stockid) {
+#ifdef UI_GTK2
+        button = gtk_tool_button_new_from_stock(item->args.stockid);
+#else
+        // TODO: gtk3 stock
+        button = gtk_tool_button_new(NULL, item->args.label);
+#endif
+    } else {
+        button = gtk_tool_button_new(NULL, item->args.label);
+    }
+    
+    gtk_tool_item_set_homogeneous(button, FALSE);
+    if(item->args.icon) {
+        set_toolbutton_icon(button, item->args.icon);
+    }
+    gtk_tool_item_set_is_important(button, TRUE);
+    
+    gtk_toolbar_insert(tb, button, -1);
+    
+    // menu
+    GtkWidget *menu_widget = gtk_menu_new();
+    ui_add_menu_items(menu_widget, 0, &item->menu, obj); 
+    gtk_widget_show_all(menu_widget);
+    
+    UiToolbarMenuWidget *tbmenu = malloc(sizeof(UiToolbarMenuWidget));
+    tbmenu->button = GTK_WIDGET(button);
+    tbmenu->menu = GTK_MENU(menu_widget);
+    
+    g_signal_connect(
+                button,
+                "clicked",
+                G_CALLBACK(ui_toolbar_menubutton_clicked),
+                tbmenu);
+    
+    g_signal_connect(
+                button,
+                "destroy",
+                G_CALLBACK(ui_toolbar_menubutton_destroy),
+                tbmenu);
+}
+
+
+
+
+// deprecated / unsupported
+/*
 void add_toolbar_combobox(GtkToolbar *tb, UiToolbarComboBox *cb, UiObject *obj) {
     UiModel *modelinfo = ui_model(obj->ctx, UI_STRING, "", -1);
     modelinfo->getvalue = cb->getvalue;
@@ -422,4 +345,76 @@
         gtk_toolbar_insert(tb, item, -1);
     }
 }
+*/
 
+
+
+#ifdef UI_GTK3
+
+GtkWidget* ui_create_headerbar(UiObject *obj) {
+    GtkWidget *headerbar = gtk_header_bar_new();
+    
+    CxMap *items = uic_get_toolbar_items();
+    CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);
+    CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);
+    CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);
+    
+    ui_headerbar_add_items(obj, headerbar, items, left_defaults);
+    ui_headerbar_add_items(obj, headerbar, items, center_defaults);
+    ui_headerbar_add_items(obj, headerbar, items, right_defaults);
+    
+    return headerbar;
+}
+
+static void hb_create_item(UiObject *obj, GtkWidget *toolbar, UiToolbarItemI *i) {
+    GtkHeaderBar *tb = GTK_HEADER_BAR(toolbar);
+    switch(i->type) {
+        case UI_TOOLBAR_ITEM: {
+            add_headerbar_item_widget(tb, (UiToolbarItem*)i, obj);
+            break;
+        }
+        case UI_TOOLBAR_TOGGLEITEM: {
+            add_headerbar_item_toggle_widget(tb, (UiToolbarToggleItem*)i, obj);
+            break;
+        }
+        case UI_TOOLBAR_MENU: {
+            add_headerbar_item_menu_widget(tb, (UiToolbarMenuItem*)i, obj);
+            break;
+        }
+        default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type);
+    }
+}
+
+
+void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxMap *items, CxList *defaults) {
+    // add pre-configured items
+    CxIterator i = cxListIterator(defaults);
+    cx_foreach(char*, def, i) {
+        UiToolbarItemI* item = uic_toolbar_get_item(def);
+        if (!item) {
+            fprintf(stderr, "unknown toolbar item: %s\n", def);
+            continue;
+        }
+        hb_create_item(obj, headerbar, item);
+    }
+}
+
+void add_headerbar_item_widget(GtkHeaderBar *hb, UiToolbarItem *item, UiObject *obj) {
+    GtkWidget *button = gtk_button_new_with_label(item->args.label);
+    if(item->args.icon) {
+        ui_button_set_icon_name(button, item->args.icon);
+    }
+    
+    gtk_header_bar_pack_start(hb, button);
+    
+}
+
+void add_headerbar_item_toggle_widget(GtkHeaderBar *hb, UiToolbarToggleItem *item, UiObject *obj) {
+    
+}
+
+void add_headerbar_item_menu_widget(GtkHeaderBar *hb, UiToolbarMenuItem *item, UiObject *obj) {
+    
+}
+
+#endif
--- a/ui/gtk/toolbar.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/toolbar.h	Sun Jun 09 15:43:08 2024 +0200
@@ -30,10 +30,10 @@
 #define	TOOLBAR_H
 
 #include "../ui/toolbar.h"
+#include "../common/toolbar.h"
 #include <cx/map.h>
 #include <cx/list.h>
 
-#include "model.h"
 #include "tree.h"
 
 #ifdef	__cplusplus
@@ -102,7 +102,6 @@
     void                *userdata;
 };
 
-void ui_toolbar_init();
 
 void ui_toolitem_vstgr(
         char *name,
@@ -114,18 +113,31 @@
 
 GtkWidget* ui_create_toolbar(UiObject *obj);
 
-void add_toolitem_widget(GtkToolbar *tb, UiToolItem *item, UiObject *obj);
-void add_toolitem_st_widget(GtkToolbar *tb, UiStToolItem *item, UiObject *obj);
-void add_toolitem_toggle_widget(GtkToolbar *tb, UiToggleToolItem *item, UiObject *obj);
+void ui_toolbar_add_items(UiObject *obj, GtkWidget *toolbar, CxMap *items, CxList *defaults);
+
+void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj);
+void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObject *obj);
+void add_toolitem_menu_widget(GtkToolbar *tb, UiToolbarMenuItem *item, UiObject *obj);
+
+void ui_tool_button_toggled(GtkToggleToolButton *widget, UiVarEventData *event);
+int64_t ui_tool_toggle_button_get(UiInteger *integer);
+void ui_tool_toggle_button_set(UiInteger *integer, int64_t value);
 
+GtkWidget* ui_create_headerbar(UiObject *obj);
+
+void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxMap *items, CxList *defaults);
+
+void add_headerbar_item_widget(GtkHeaderBar *hb, UiToolbarItem *item, UiObject *obj);
+void add_headerbar_item_toggle_widget(GtkHeaderBar *hb, UiToolbarToggleItem *item, UiObject *obj);
+void add_headerbar_item_menu_widget(GtkHeaderBar *hb, UiToolbarMenuItem *item, UiObject *obj);
+
+
+/*
 void add_toolbar_combobox(GtkToolbar *tb, UiToolbarComboBox *cb, UiObject *obj);
 void add_toolbar_combobox_nv(GtkToolbar *tb, UiToolbarComboBoxNV *cb, UiObject *obj);
 void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e);
 void ui_combobox_update(UiEvent *event, void *combobox);
-
-void ui_tool_button_toggled(GtkToggleToolButton *widget, UiEventData *event);
-int64_t ui_tool_toggle_button_get(UiInteger *integer);
-void ui_tool_toggle_button_set(UiInteger *integer, int64_t value);
+*/
 
 #ifdef	__cplusplus
 }
--- a/ui/gtk/toolkit.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/toolkit.c	Sun Jun 09 15:43:08 2024 +0200
@@ -33,10 +33,12 @@
 
 #include "toolkit.h"
 #include "toolbar.h"
-#include "model.h"
 #include "image.h"
 #include "../common/document.h"
 #include "../common/properties.h"
+#include "../common/menu.h"
+#include "../common/toolbar.h"
+#include "../common/threadpool.h"
 
 #include <cx/utils.h>
 #include <cx/string.h>
@@ -70,14 +72,12 @@
     gtk_init(&argc, &argv);
     application_name = appname;
     
+    ui_css_init();
+    
     uic_docmgr_init();
-    ui_toolbar_init(); // TODO: remove
     
     uic_toolbar_init();
     
-    // init custom types
-    ui_list_init();
-    
     ui_image_init();
     
     uic_load_app_properties();
@@ -191,6 +191,23 @@
     return NULL;
 }
 
+static gboolean ui_idle_func(void *data) {
+    UiJob *job = data;
+    job->job_func(job->job_data);
+    free(job);
+    return FALSE;
+}
+
+void ui_call_mainthread(ui_threadfunc tf, void* td) {
+    UiJob *job = malloc(sizeof(UiJob));
+    job->job_func = tf;
+    job->job_data = td;
+    job->finish_callback = NULL;
+    job->finish_data = NULL;
+    job->obj = NULL;
+    g_idle_add(ui_idle_func, job);
+}
+
 void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) {
     UiJob *job = malloc(sizeof(UiJob));
     job->obj = obj;
@@ -245,7 +262,9 @@
 }
 
 void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data) {
-    ui_destroy_boundvar(data->obj->ctx, data->var);
+    if(data->var) {
+        ui_destroy_boundvar(data->obj->ctx, data->var);
+    }
     free(data);
 }
 
@@ -270,3 +289,28 @@
 }
 
 
+#if GTK_MAJOR_VERSION >= 3
+
+static GtkCssProvider* ui_gtk_css_provider;
+
+static const char *ui_gtk_css = 
+"#path-textfield-box {"
+"  background-color: @theme_base_color;"
+"  border-radius: 5px;"
+"  padding: 0px;"
+"}";
+
+void ui_css_init(void) {
+    ui_gtk_css_provider = gtk_css_provider_new();
+    gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -1, 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_PRIORITY_USER);
+}
+
+
+
+#endif
\ No newline at end of file
--- a/ui/gtk/toolkit.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/toolkit.h	Sun Jun 09 15:43:08 2024 +0200
@@ -44,23 +44,17 @@
     ui_callback callback;
     void        *userdata;
     int         value;
+    void        *customdata;
 } UiEventData;
 
 typedef struct UiVarEventData {
-    UiObject   *obj;
-    UiVar     *var;
-    UiObserver **observers;
+    UiObject    *obj;
+    UiVar       *var;
+    UiObserver  **observers;
+    ui_callback callback;
+    void        *userdata;
 } UiVarEventData;
 
-
-typedef struct UiJob {
-    UiObject      *obj;
-    ui_threadfunc job_func;
-    void          *job_data;
-    ui_callback   finish_callback;
-    void          *finish_data;
-} UiJob;
-
 struct UiSelection {
     GtkSelectionData *data;
 };
@@ -82,6 +76,10 @@
 void ui_set_active_window(UiObject *obj);
 UiObject *ui_get_active_window();
 
+#if GTK_MAJOR_VERSION >= 3
+void ui_css_init(void);
+#endif
+
 #ifdef	__cplusplus
 }
 #endif
--- a/ui/gtk/tree.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/tree.c	Sun Jun 09 15:43:08 2024 +0200
@@ -36,16 +36,117 @@
 #include "container.h"
 
 #include "tree.h"
+#include "image.h"
 
 
 void* ui_strmodel_getvalue(void *elm, int column) {
     return column == 0 ? elm : NULL;
 }
 
+static GtkListStore* create_list_store(UiList *list, UiModel *model) {
+    int columns = model->columns;
+    GType types[2*columns];
+    int c = 0;
+    for(int i=0;i<columns;i++,c++) {
+        switch(model->types[i]) {
+            case UI_STRING: 
+            case UI_STRING_FREE: types[c] = G_TYPE_STRING; break;
+            case UI_INTEGER: types[c] = G_TYPE_INT; break;
+            case UI_ICON: types[c] = G_TYPE_OBJECT; break;
+            case UI_ICON_TEXT: 
+            case UI_ICON_TEXT_FREE: {
+                types[c] = G_TYPE_OBJECT;
+                types[++c] = G_TYPE_STRING;
+            }
+        }
+    }
+    
+    GtkListStore *store = gtk_list_store_newv(c, types);
+    
+    if(list) {
+        void *elm = list->first(list);
+	while(elm) {
+            // insert new row
+            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);
+                        int *intptr = data;
+                        g_value_set_int(&value, *intptr);
+                        break;
+                    }
+                    case UI_ICON: {
+                        g_value_init(&value, G_TYPE_OBJECT);
+                        UiIcon *icon = data;
+                        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);
+                        }
+
+                        
+                        break;
+                    }
+                    case UI_ICON_TEXT:
+                    case UI_ICON_TEXT_FREE: {
+                        GValue pixbufvalue = G_VALUE_INIT;
+                        UiIcon *icon = data;
+                        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);
+                        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);
+            }
+            
+            // next row
+            elm = list->next(list);
+        }
+    }
+    
+    return store;
+}
 
 
-
-UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
+UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
     // create treeview
     GtkWidget *view = gtk_tree_view_new();
     GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
@@ -64,9 +165,12 @@
 #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);
+    model->getvalue = args.getvalue;
+    
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST);
+    
+    UiList *list = var ? var->value : NULL;
+    GtkListStore *listmodel = create_list_store(list, model);
     gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel));
     
     UiListView *listview = malloc(sizeof(UiListView));
@@ -85,11 +189,11 @@
     list->obj = listview;
     
     // add callback
-    if(f) {
+    if(args.onactivate) {
         UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData));
         event->obj = obj;
-        event->userdata = udata;
-        event->activate = f;
+        event->activatedata = args.onactivatedata;
+        event->activate = args.onactivate;
         event->selection = NULL;
 
         g_signal_connect(
@@ -107,33 +211,16 @@
             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);
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, scroll_area, FALSE);
     
     // ct->current should point to view, not scroll_area, to make it possible
     // to add a context menu
-    ct->current = view;
+    current->container->current = view;
     
     return scroll_area;
 }
 
-UIWIDGET ui_listview_deprecated(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = list;
-    var->type = UI_VAR_SPECIAL;
-    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");
     
@@ -156,12 +243,17 @@
       { "text/uri-list", 0, 2 },
     };
 
-UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb) {
+UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
     // create treeview
     GtkWidget *view = gtk_tree_view_new();
     
+    UiModel *model = args.model;
+    int columns = model ? model->columns : 0;
+    
     int addi = 0;
-    for(int i=0;i<model->columns;i++) {
+    for(int i=0;i<columns;i++) {
         GtkTreeViewColumn *column = NULL;
         if(model->types[i] == UI_ICON_TEXT) {
             column = gtk_tree_view_column_new();
@@ -178,6 +270,14 @@
             gtk_tree_view_column_add_attribute(column, textrenderer, "text", i+1);
             
             addi++;
+        } else if (model->types[i] == UI_ICON) {
+            GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
+            column = gtk_tree_view_column_new_with_attributes(
+                model->titles[i],
+                iconrenderer,
+                "pixbuf",
+                i + addi,
+                NULL);
         } else {
             GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
             column = gtk_tree_view_column_new_with_attributes(
@@ -198,8 +298,10 @@
     
 #endif
     
-    UiList *list = var->value;
-    UiListModel *listmodel = ui_list_model_new(obj, var, model);
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST);
+    
+    UiList *list = var ? var->value : NULL;
+    GtkListStore *listmodel = create_list_store(list, model);
     gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel));
     
     //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL);
@@ -225,17 +327,18 @@
     // 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) {
+    event->activate = args.onactivate;
+    event->selection = args.onselection;
+    event->activatedata = args.onactivatedata;
+    event->selectiondata = args.onselectiondata;
+    if(args.onactivate) {
         g_signal_connect(
                 view,
                 "row-activated",
                 G_CALLBACK(ui_listview_activate_event),
                 event);
     }
-    if(cb.selection) {
+    if(args.onselection) {
         GtkTreeSelection *selection = gtk_tree_view_get_selection(
                 GTK_TREE_VIEW(view));
         g_signal_connect(
@@ -258,32 +361,16 @@
             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);
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, scroll_area, FALSE);
     
     // ct->current should point to view, not scroll_area, to make it possible
     // to add a context menu
-    ct->current = view;
+    current->container->current = view;
     
     return scroll_area;
 }
 
-UIWIDGET ui_table_deprecated(UiObject *obj, UiList *list, UiModel *model, UiListCallbacks cb) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = list;
-    var->type = UI_VAR_SPECIAL;
-    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));
@@ -370,9 +457,9 @@
  
 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));
+    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));
     // TODO: free old model
 }
 
@@ -407,7 +494,7 @@
     e.document = event->obj->ctx->document;
     e.eventdata = selection;
     e.intval = selection->count > 0 ? selection->rows[0] : -1;
-    event->activate(&e, event->userdata);
+    event->activate(&e, event->activatedata);
     
     if(selection->count > 0) {
         free(selection->rows);
@@ -427,7 +514,7 @@
     e.document = event->obj->ctx->document;
     e.eventdata = selection;
     e.intval = selection->count > 0 ? selection->rows[0] : -1;
-    event->selection(&e, event->userdata);
+    event->selection(&e, event->selectiondata);
     
     if(selection->count > 0) {
         free(selection->rows);
@@ -468,42 +555,37 @@
 
 /* --------------------------- ComboBox ---------------------------  */
 
-UIWIDGET ui_combobox_deprecated(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) {
-    UiVar *var = malloc(sizeof(UiVar));
-    var->value = list;
-    var->type = UI_VAR_SPECIAL;
-    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) {
+UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) {
+    UiObject* current = uic_current_obj(obj);
+    
     UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
-    model->getvalue = getvalue;
-    UiListModel *listmodel = ui_list_model_new(obj, var, model);
+    model->getvalue = args.getvalue;
+    
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST);
     
-    GtkWidget *combobox = ui_create_combobox(obj, listmodel, f, udata);
-    UiContainer *ct = uic_get_current_container(obj);
-    ct->add(ct, combobox, FALSE);
+    GtkWidget *combobox = ui_create_combobox(obj, model, var, args.onactivate, args.onactivatedata);
+    UI_APPLY_LAYOUT1(current, args);
+    current->container->add(current->container, combobox, FALSE);
+    current->container->current = combobox;
     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));
-    
+GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f, void *udata) {
+    GtkWidget *combobox = gtk_combo_box_new();
+     
     UiListView *uicbox = malloc(sizeof(UiListView));
     uicbox->obj = obj;
     uicbox->widget = combobox;
-    uicbox->var = model->var;
-    uicbox->model = model->model;
+    
+    UiList *list = var ? var->value : NULL;
+    GtkListStore *listmodel = create_list_store(list, model);
+    
+    if(listmodel) {
+        gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(listmodel));
+    }
+    
+    uicbox->var = var;
+    uicbox->model = model;
     
     g_signal_connect(
                 combobox,
@@ -512,9 +594,10 @@
                 uicbox);
     
     // bind var
-    UiList *list = model->var->value;
-    list->update = ui_combobox_modelupdate;
-    list->obj = uicbox;
+    if(list) {
+        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);
@@ -533,6 +616,7 @@
         event->userdata = udata;
         event->callback = f;
         event->value = 0;
+        event->customdata = NULL;
 
         g_signal_connect(
                 combobox,
@@ -556,7 +640,7 @@
 
 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));
+    GtkListStore *store = create_list_store(view->var->value, view->model);
+    gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(store));
 }
 
--- a/ui/gtk/tree.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/tree.h	Sun Jun 09 15:43:08 2024 +0200
@@ -31,7 +31,6 @@
 
 #include "../ui/tree.h"
 #include "toolkit.h"
-#include "model.h"
 
 #ifdef	__cplusplus
 extern "C" {
@@ -48,7 +47,8 @@
     UiObject    *obj;
     ui_callback activate;
     ui_callback selection;
-    void        *userdata;
+    void        *activatedata;
+    void        *selectiondata;
 } UiTreeEventData;
     
 void* ui_strmodel_getvalue(void *elm, int column);
@@ -76,7 +76,7 @@
 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);
+GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f, void *udata);
 void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e);
 void ui_combobox_modelupdate(UiList *list, int i);
         
--- a/ui/gtk/window.c	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/gtk/window.c	Sun Jun 09 15:43:08 2024 +0200
@@ -126,6 +126,9 @@
                 gtk_box_pack_start(GTK_BOX(vbox), tb, FALSE, FALSE, 0);
             }
         }
+        
+        //GtkWidget *hb = ui_create_headerbar(obj);
+        //gtk_window_set_titlebar(GTK_WINDOW(obj->widget), hb);
     }
     
     // window content
@@ -148,41 +151,171 @@
     return create_window(title, window_data, FALSE);
 }
 
-UiObject* ui_simplewindow(const char *title, void *window_data) {
+UiObject* ui_simple_window(const char *title, void *window_data) {
     return create_window(title, window_data, TRUE);
 }
 
-static char* ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action) {
+void ui_window_size(UiObject *obj, int width, int height) {
+    gtk_window_set_default_size(
+                GTK_WINDOW(obj->widget),
+                width,
+                height);
+}
+
+static void ui_dialog_response (GtkDialog* self, gint response_id, gpointer user_data) {
+    UiEventData *data = user_data;
+    UiEvent evt;
+    evt.obj = data->obj;
+    evt.document = evt.obj->ctx->document;
+    evt.window = evt.obj->window;
+    evt.eventdata = NULL;
+    evt.intval = 0;
+    
+    if(data->customdata) {
+        GtkWidget *entry = data->customdata;
+        evt.eventdata = (void*)gtk_entry_get_text(GTK_ENTRY(entry));
+        
+    }
+    
+    if(response_id == 1 || response_id == 2) {
+        evt.intval = response_id;
+    }
+    
+    
+    if(data->callback) {
+        data->callback(&evt, data->userdata);
+    }
+    
+    gtk_widget_destroy(GTK_WIDGET(self));
+}
+
+void ui_dialog_create(UiObject *parent, UiDialogArgs args) {
+    GtkDialog *dialog = GTK_DIALOG(gtk_dialog_new());
+    GtkWidget *dialog_w = GTK_WIDGET(dialog);
+    if(args.title) {
+        gtk_window_set_title(GTK_WINDOW(dialog), args.title);
+    }
+    if(args.button1_label) {
+        gtk_dialog_add_button(dialog, args.button1_label, 1);
+    }
+    if(args.button2_label) {
+        gtk_dialog_add_button(dialog, args.button2_label, 2);
+    }
+    if(args.closebutton_label) {
+        gtk_dialog_add_button(dialog, args.closebutton_label, 0);
+    }
+    
+    GtkWidget *content_area = gtk_dialog_get_content_area(dialog);
+    if(args.content) {
+        GtkWidget *label = gtk_label_new(args.content);
+        gtk_container_add(GTK_CONTAINER(content_area), label);
+    }
+    
+    GtkWidget *textfield = NULL;
+    if(args.input) {
+        textfield = gtk_entry_new();
+        gtk_container_add(GTK_CONTAINER(content_area), textfield);
+    }
+    
+    UiEventData *event = malloc(sizeof(UiEventData));
+    event->obj = parent;
+    event->callback = args.result;
+    event->userdata = args.resultdata;
+    event->value = 0;
+    event->customdata = textfield;
+    
+    g_signal_connect(dialog_w,
+                           "response",
+                           G_CALLBACK(ui_dialog_response),
+                           event);
+    
+    gtk_widget_show_all(GTK_WIDGET(dialog_w));
+}
+
+static void ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action, unsigned int mode, ui_callback file_selected_callback, void *cbdata) {
     char *button;
     char *title;
     
-    if(action == GTK_FILE_CHOOSER_ACTION_OPEN) {
-        button = GTK_STOCK_OPEN;
-        title = "Datei öffnen...";
+    GtkWidget *dialog;
+    if((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) {
+        dialog = gtk_file_chooser_dialog_new (
+                "Open Folder",
+                GTK_WINDOW(obj->widget),
+                GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+                "Cancel",
+                GTK_RESPONSE_CANCEL,
+                "Select Folder",
+                GTK_RESPONSE_ACCEPT,
+                NULL);
+    } else if(action == GTK_FILE_CHOOSER_ACTION_OPEN) {
+        dialog = gtk_file_chooser_dialog_new (
+                "Select Folder",
+                GTK_WINDOW(obj->widget),
+                action,
+                "Cancel",
+                GTK_RESPONSE_CANCEL,
+                "Open File",
+                GTK_RESPONSE_ACCEPT,
+                NULL);
     } else {
-        button = GTK_STOCK_SAVE;
-        title = "Datei speichern...";
+        dialog = gtk_file_chooser_dialog_new (
+                "Save File",
+                GTK_WINDOW(obj->widget),
+                action,
+                "Cancel",
+                GTK_RESPONSE_CANCEL,
+                "Save File",
+                GTK_RESPONSE_ACCEPT,
+                NULL);
     }
     
-    GtkWidget *dialog = gtk_file_chooser_dialog_new(
-                                title,
-                                GTK_WINDOW(obj->widget),
-                                action,
-                                GTK_STOCK_CANCEL,
-                                GTK_RESPONSE_CANCEL,
-                                button,
-                                GTK_RESPONSE_ACCEPT,
-                                NULL);
-    if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
-        char *file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
-        gtk_widget_destroy(dialog);
-        char *copy = strdup(file);
-        g_free(file);
-        return copy;
-    } else {
-        gtk_widget_destroy(dialog);
-        return NULL;
+    if((mode & UI_FILEDIALOG_SELECT_MULTI) == UI_FILEDIALOG_SELECT_MULTI) {
+        gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
     }
+       
+    UiEvent evt;
+    evt.obj = obj;
+    evt.document = evt.obj->ctx->document;
+    evt.window = evt.obj->window;
+    evt.intval = 0;
+    
+    UiFileList flist;
+    flist.files = NULL;
+    flist.nfiles = 0;
+    evt.eventdata = &flist;
+    
+    int result = gtk_dialog_run(GTK_DIALOG (dialog));
+    GSList *selection = NULL;
+    if(result == GTK_RESPONSE_ACCEPT) {
+        selection = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+        flist.nfiles = g_slist_length(selection);
+        flist.files = calloc(flist.nfiles, sizeof(char*));
+        int i = 0;
+        while(selection) {
+            flist.files[i] = selection->data;
+            selection = selection->next;
+            i++;
+        }
+    }
+    
+    if(file_selected_callback) {
+        file_selected_callback(&evt, cbdata);
+    }
+    
+    for(int i=0;i<flist.nfiles;i++) {
+        g_free(flist.files[i]);
+    } 
+    free(flist.files);
+    g_slist_free(selection);
+    
+    gtk_widget_destroy(dialog);
 }
 
+void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) {
+    ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_OPEN, mode, file_selected_callback, cbdata);
+}
 
+void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata) {
+    ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_SAVE, 0, file_selected_callback, cbdata);
+}
+
--- a/ui/ui/display.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/ui/display.h	Sun Jun 09 15:43:08 2024 +0200
@@ -97,8 +97,8 @@
 UIEXPORT UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs args);
 UIEXPORT UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs args);
 
-UIWIDGET ui_space(UiObject *obj);
-UIWIDGET ui_separator(UiObject *obj);
+UIWIDGET ui_space_deprecated(UiObject *obj);
+UIWIDGET ui_separator_deprecated(UiObject *obj);
 
 /* progress bar/spinner */
 
--- a/ui/ui/entry.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/ui/entry.h	Sun Jun 09 15:43:08 2024 +0200
@@ -35,13 +35,29 @@
 extern "C" {
 #endif
 
-UIWIDGET ui_spinner(UiObject *obj, int step, UiInteger *i);
-UIWIDGET ui_spinnerf(UiObject *obj, double step, int digits, UiDouble *d);
-UIWIDGET ui_spinnerr(UiObject *obj, UiRange *r);
+
+typedef struct UiSpinnerArgs {
+    UiTri fill;
+    UiBool hexpand;
+    UiBool vexpand;
+    int colspan;
+    int rowspan;
 
-UIWIDGET ui_spinner_nv(UiObject *obj, int step, char *varname);
-UIWIDGET ui_spinnerf_nv(UiObject *obj, double step, int digits, char *varname);
-UIWIDGET ui_spinnerr_nv(UiObject *obj, char *varname);
+    double step;
+    int digits;
+    UiInteger *intvalue;
+    UiDouble* doublevalue;
+    UiRange *rangevalue;
+    const char* varname;
+    ui_callback onchange;
+    void* onchangedata;
+} UiSpinnerArgs;
+
+
+    
+UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs args);
+
+#define ui_spinner(obj, ...) ui_spinner_create(obj, (UiSpinnerArgs){ __VA_ARGS__ } )
 
 void ui_spinner_setrange(UIWIDGET spinner, double min, double max);
 void ui_spinner_setdigits(UIWIDGET spinner, int digits);
--- a/ui/ui/text.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/ui/text.h	Sun Jun 09 15:43:08 2024 +0200
@@ -84,8 +84,8 @@
     void* ondropsdata;
 } UiPathTextFieldArgs;
 
-UIWIDGET ui_textarea(UiObject *obj, UiText *value);
-UIWIDGET ui_textarea_nv(UiObject *obj, char *varname);
+UIWIDGET ui_textarea_deprecated(UiObject *obj, UiText *value);
+UIWIDGET ui_textarea_nv_deprecated(UiObject *obj, char *varname);
 
 UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea);
 
--- a/ui/ui/toolkit.h	Mon Feb 12 21:13:23 2024 +0100
+++ b/ui/ui/toolkit.h	Sun Jun 09 15:43:08 2024 +0200
@@ -499,6 +499,7 @@
 UIEXPORT void ui_listselection_free(UiListSelection selection);
 
 UIEXPORT UiIcon* ui_icon(const char* name, size_t size);
+UIEXPORT UiIcon* ui_icon_unscaled(const char *name, int size);
 UIEXPORT UiIcon* ui_imageicon(const char* file);
 UIEXPORT void ui_icon_free(UiIcon* icon);
 

mercurial